{ "updatedAt": "2026-06-20T16:19:11.789Z", "createdAt": "2026-06-08T20:09:28.132Z", "id": "sXH75CXIQ0sEzm1Y", "name": "Chat de WhatsApp - Propuestas Ejecutadas", "description": null, "active": true, "isArchived": false, "nodes": [ { "parameters": { "method": "POST", "url": "={{'https://wsp.gomezleemarketing.com'}}/message/sendText/{{'botsoporte'}}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "apikey", "value": "={{'C267126ABB45-4C12-B626-6BAB1833F5D7'}}" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ { "name": "number", "value": "={{ (() => {\n const limpiar = (valor) => String(valor ?? '').trim();\n\n const candidatos = [\n $json.whatsapp_to,\n $json.group_jid,\n $json.whatsapp_remote_jid,\n $json.sesion_activa?.group_jid,\n $json.sesion_activa?.whatsapp_to,\n $json.sesion_activa?.whatsapp_remote_jid,\n $json.manager_telefono,\n $json.sender_phone\n ];\n\n let raw = limpiar(candidatos.find(v => limpiar(v)));\n\n if (!raw) return '';\n\n raw = raw.replace('@c.us', '@s.whatsapp.net');\n\n // Si ya viene como grupo, conservarlo.\n if (raw.includes('@g.us')) {\n return raw;\n }\n\n // Si viene como usuario WhatsApp, validar si realmente era un grupo.\n if (raw.includes('@s.whatsapp.net')) {\n const numero = raw.replace('@s.whatsapp.net', '').replace(/\\D/g, '');\n\n // Los grupos de WhatsApp suelen venir como 120363...\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n }\n\n const numero = raw.replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n})() }}" }, { "name": "delay", "value": "={{ 1000 }}" }, { "name": "text", "value": "={{ $json.whatsapp_text || $json.telegram_text || $json.texto_respuesta || $json.mensaje || $json.text || 'Mensaje sin contenido' }}" } ] }, "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ 9984, 704 ], "id": "b1d4c2c3-c77f-4b9d-b4ca-c40535c26299", "name": "WhatsApp - Enviar mensaje TEST" }, { "parameters": { "jsCode": "const input = $json || {};\nconst body = input.body || input;\nconst data = body.data || {};\n\nconst texto = (valor) => String(valor ?? '').trim();\n\nconst limpiarNumero = (valor) =>\n texto(valor)\n .replace('@s.whatsapp.net', '')\n .replace('@c.us', '')\n .replace('@g.us', '')\n .replace('@lid', '')\n .replace(/\\D/g, '');\n\nconst key = data.key || {};\nconst message = data.message || {};\n\n// --------------------------------------------------\n// 1. Identificar origen real del mensaje\n// --------------------------------------------------\n\nconst remoteJid = texto(\n key.remoteJid ||\n data.remoteJid ||\n body.remoteJid ||\n ''\n);\n\nconst isGroup = remoteJid.endsWith('@g.us');\n\nconst participantRaw = texto(\n key.participant ||\n data.participant ||\n body.participant ||\n data.sender ||\n data.participantJid ||\n ''\n);\n\nconst participantAlt = texto(\n key.participantAlt ||\n data.participantAlt ||\n body.participantAlt ||\n data.senderAlt ||\n ''\n);\n\n// En grupos, Evolution puede mandar:\n// participant = 1655...@lid\n// participantAlt = número real@s.whatsapp.net\n// Para sesiones y permisos necesitamos preferir el número real.\nconst participantJid = participantAlt || participantRaw;\n\n// En grupo, el sender real es participantAlt si existe.\n// En chat individual, el sender real es remoteJid.\nconst senderJid = isGroup\n ? participantJid\n : remoteJid;\n\nconst senderPhone = limpiarNumero(senderJid);\nconst groupJid = isGroup ? remoteJid : '';\n\nconst pushName =\n texto(data.pushName) ||\n texto(body.pushName) ||\n texto(data.senderName) ||\n texto(body.senderName) ||\n 'Usuario WhatsApp';\n\n// El bot debe responder al grupo si el mensaje vino de grupo.\n// Si vino de chat individual, responde al usuario.\nconst whatsappTo = isGroup ? groupJid : remoteJid;\n\n// --------------------------------------------------\n// 2. Extraer texto o payload de botones\n// --------------------------------------------------\n\nconst buttonText =\n texto(message.buttonsResponseMessage?.selectedButtonId) ||\n texto(message.buttonsResponseMessage?.selectedDisplayText) ||\n texto(message.listResponseMessage?.singleSelectReply?.selectedRowId) ||\n texto(message.listResponseMessage?.title) ||\n texto(message.templateButtonReplyMessage?.selectedId) ||\n texto(message.templateButtonReplyMessage?.selectedDisplayText) ||\n '';\n\nconst textoMensaje =\n buttonText ||\n texto(message.conversation) ||\n texto(message.extendedTextMessage?.text) ||\n texto(data.messageText) ||\n texto(data.text) ||\n texto(body.text) ||\n '';\n\n// --------------------------------------------------\n// 3. Detectar media\n// --------------------------------------------------\n\nconst audioMsg = message.audioMessage;\nconst imageMsg = message.imageMessage;\nconst videoMsg = message.videoMessage;\nconst documentMsg = message.documentMessage;\n\nconst documentMime = texto(documentMsg?.mimetype);\nconst documentFileName = texto(documentMsg?.fileName);\n\nlet messageType = 'unknown';\n\nif (audioMsg) {\n messageType = 'audio';\n} else if (imageMsg) {\n messageType = 'image';\n} else if (videoMsg) {\n messageType = 'video';\n} else if (documentMsg && documentMime.startsWith('video/')) {\n messageType = 'video';\n} else if (documentMsg && documentMime.startsWith('image/')) {\n messageType = 'image';\n} else if (documentMsg && documentMime.startsWith('audio/')) {\n messageType = 'audio';\n} else if (documentMsg) {\n messageType = 'document';\n} else if (textoMensaje) {\n messageType = 'text';\n}\n\nconst tieneMedia = ['audio', 'image', 'video', 'document'].includes(messageType);\n\nconst mediaMimeType =\n texto(audioMsg?.mimetype) ||\n texto(imageMsg?.mimetype) ||\n texto(videoMsg?.mimetype) ||\n documentMime ||\n '';\n\nconst mediaSourceId =\n texto(key.id) ||\n texto(data.id) ||\n texto(data.messageId) ||\n '';\n\n// --------------------------------------------------\n// 4. Normalizar comandos internos\n// --------------------------------------------------\n\nconst normalizarTexto = (valor) =>\n texto(valor)\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toUpperCase();\n\nconst normalizarComando = (valor) => {\n const t = normalizarTexto(valor);\n\n // Nuevo inicio oficial\n if (t === 'HEY') return 'START';\n\n // Compatibilidad temporal\n if (\n t === '/START' ||\n t === 'START' ||\n t === 'INICIAR'\n) {\n return 'START';\n}\n\n if (t === 'CANCELAR' || t === 'CANCEL') {\n return 'CANCELAR';\n }\n\n if (\n t === 'FOTOS LISTAS' ||\n t === 'FOTOS_LISTAS' ||\n t === 'IMAGENES LISTAS' ||\n t === 'IMAGENES_LISTAS' ||\n t === 'PHOTOS READY' ||\n t === 'PHOTOS_READY'\n ) {\n return 'FOTOS_LISTAS';\n }\n\n if (\n t === 'SIN VIDEO' ||\n t === 'SIN_VIDEO' ||\n t === 'NO VIDEO' ||\n t === 'NO_VIDEO'\n ) {\n return 'SIN_VIDEO';\n }\n\n if (\n t === 'LISTO' ||\n t === 'DONE' ||\n t === 'FINISH' ||\n t === 'FINALIZAR'\n ) {\n return 'LISTO';\n }\n\n return 'NORMAL';\n};\n\n// --------------------------------------------------\n// 5. Detectar país/idioma base por teléfono\n// --------------------------------------------------\n\nconst detectarIdiomaPorTelefono = (phone) => {\n const n = limpiarNumero(phone);\n\n // Países GLM principalmente hispanohablantes\n if (n.startsWith('502')) return { country_code: '502', pais_detectado: 'Guatemala', idioma_flujo: 'ES' };\n if (n.startsWith('503')) return { country_code: '503', pais_detectado: 'El Salvador', idioma_flujo: 'ES' };\n if (n.startsWith('504')) return { country_code: '504', pais_detectado: 'Honduras', idioma_flujo: 'ES' };\n if (n.startsWith('505')) return { country_code: '505', pais_detectado: 'Nicaragua', idioma_flujo: 'ES' };\n if (n.startsWith('506')) return { country_code: '506', pais_detectado: 'Costa Rica', idioma_flujo: 'ES' };\n if (n.startsWith('507')) return { country_code: '507', pais_detectado: 'Panamá', idioma_flujo: 'ES' };\n\n // República Dominicana: +1 809 / 829 / 849\n if (n.startsWith('1809') || n.startsWith('1829') || n.startsWith('1849')) {\n return { country_code: '1', pais_detectado: 'República Dominicana', idioma_flujo: 'ES' };\n }\n\n // Default para +1 no dominicano: inglés hasta que hagamos tabla formal\n if (n.startsWith('1')) {\n return { country_code: '1', pais_detectado: 'País +1 no identificado', idioma_flujo: 'EN' };\n }\n\n return { country_code: '', pais_detectado: 'No identificado', idioma_flujo: 'ES' };\n};\n\nconst idiomaInfo = detectarIdiomaPorTelefono(senderPhone);\n\n// --------------------------------------------------\n// 6. Resultado normalizado\n// --------------------------------------------------\n\nreturn [\n {\n json: {\n event_id: texto(key.id || data.id || Date.now()),\n fecha_recepcion: new Date().toISOString(),\n\n canal_origen: 'WHATSAPP',\n\n // Compatibilidad con el flujo actual:\n // seguimos llenando manager_telefono / manager_nombre,\n // pero ahora apuntan al sender real, no al grupo.\n manager_telefono: senderPhone,\n manager_nombre: pushName,\n usuario_id_origen: senderPhone,\n\n // Campos nuevos para grupo\n is_group: isGroup,\n group_jid: groupJid,\n group_name: texto(data.groupName || body.groupName || ''),\n sender_jid: senderJid,\n sender_phone: senderPhone,\n sender_name: pushName,\n\n whatsapp_remote_jid: remoteJid,\n whatsapp_to: whatsappTo,\n\n from_me: Boolean(key.fromMe || data.fromMe || false),\n\n message_type: messageType,\n texto: textoMensaje,\n accion_flujo: normalizarComando(textoMensaje),\n\n tiene_media: tieneMedia,\n media_count: tieneMedia ? 1 : 0,\n media_source_id: mediaSourceId,\n media_mime_type: mediaMimeType,\n media_file_name: documentFileName,\n\n country_code: idiomaInfo.country_code,\n pais_detectado: idiomaInfo.pais_detectado,\n idioma_flujo: idiomaInfo.idioma_flujo,\n\n raw_preview: JSON.stringify(body).slice(0, 4000),\n raw_event: body,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -9200, -608 ], "id": "35e401ba-acce-429c-b811-58a45c032869", "name": "Code - Normalizar evento WhatsApp TEST" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "f2be1fb5-b047-4614-a1f2-9a4d65ef5cc7", "leftValue": "={{ $json.manager_telefono === '18298946645' || $json.sender_phone === '18298946645' }}", "rightValue": "={{ $json.manager_telefono === '18298946645' }}", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ -8928, -608 ], "id": "3b65e795-a7f8-415c-8c04-7cde544c4350", "name": "Mi numero TESTING" }, { "parameters": { "httpMethod": "POST", "path": "factura", "options": {} }, "type": "n8n-nodes-base.webhook", "typeVersion": 2.1, "position": [ -9408, -608 ], "id": "bbdd46ca-d2f0-4c42-8e14-d9591168cf4d", "name": "Webhook - Evolution WhatsApp TEST", "webhookId": "e2e6c9c2-6c1e-453d-85f1-271056935828" }, { "parameters": { "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -8560, -624 ], "id": "653ca576-1661-49aa-80cb-62b7ec46a1c0", "name": "Sheets - Leer sesiones existentes", "alwaysOutputData": true, "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const evento = $('Code - Normalizar evento WhatsApp TEST').first().json || {};\nconst sesiones = $input.all().map((item) => item.json || {});\n\nconst texto = (valor) => String(valor ?? '').trim();\n\nconst estadosAbiertos = new Set([\n 'ACTIVA',\n 'PENDIENTE',\n 'EN_PROCESO',\n 'PROCESANDO',\n 'LISTO_PARA_RECUPERAR_MEDIA',\n 'LISTO_PARA_ANALIZAR',\n]);\n\nconst etapasAbiertas = new Set([\n 'ESPERANDO_AUDIO',\n 'ESPERANDO_IMAGENES',\n 'ESPERANDO_VIDEOS',\n 'PROCESANDO',\n]);\n\nconst managerTelefono = texto(evento.manager_telefono || evento.sender_phone);\nconst senderPhone = texto(evento.sender_phone || managerTelefono);\nconst groupJid = texto(evento.group_jid);\nconst isGroup = Boolean(evento.is_group);\nconst accion = texto(evento.accion_flujo || 'NORMAL');\n\n// --------------------------------------------------\n// Buscar sesión activa\n// --------------------------------------------------\n// En grupo: sesión única por grupo + sender.\n// En chat individual: sesión por manager_telefono.\nconst sesionActiva = sesiones\n .filter((row) => {\n const rowManager = texto(row.manager_telefono);\n const rowSender = texto(row.sender_phone || row.manager_telefono);\n const rowGroup = texto(row.group_jid);\n\n if (isGroup) {\n return rowGroup === groupJid && rowSender === senderPhone;\n }\n\n return rowManager === managerTelefono;\n })\n .filter((row) => {\n const estado = texto(row.estado).toUpperCase();\n const etapa = texto(row.etapa).toUpperCase();\n\n return estadosAbiertos.has(estado) || etapasAbiertas.has(etapa);\n })\n .sort((a, b) => {\n return new Date(b.ultima_actividad || b.fecha_inicio || 0) - new Date(a.ultima_actividad || a.fecha_inicio || 0);\n })[0] || null;\n\n// --------------------------------------------------\n// Crear IDs\n// --------------------------------------------------\n\nconst suffix = Math.random().toString(36).slice(2, 6).toUpperCase();\nconst phoneSuffix = senderPhone.slice(-4) || managerTelefono.slice(-4) || '0000';\nconst sessionId = `WA_${Date.now()}_${phoneSuffix}_${suffix}`;\n\nlet decision = '';\n\nif (accion === 'START') {\n decision = sesionActiva ? 'AVISO_SESION_ACTIVA' : 'CREAR_SESION';\n} else if (accion === 'CANCELAR') {\n decision = sesionActiva ? 'CANCELAR_SESION' : 'SIN_SESION_PARA_CANCELAR';\n} else {\n decision = sesionActiva ? 'CONTINUAR_SESION' : 'SIN_SESION_ACTIVA';\n}\n\nreturn [\n {\n json: {\n ...evento,\n\n sesion_activa_encontrada: Boolean(sesionActiva),\n sesion_activa: sesionActiva,\n\n session_id: sesionActiva ? texto(sesionActiva.session_id) : sessionId,\n ejecucion_id: sesionActiva ? texto(sesionActiva.ejecucion_id || sesionActiva.session_id) : sessionId,\n\n decision_flujo: decision,\n\n nueva_sesion: {\n session_id: sessionId,\n\n manager_telefono: managerTelefono,\n manager_nombre: texto(evento.manager_nombre),\n\n canal_origen: 'WHATSAPP',\n fecha_inicio: new Date().toISOString(),\n ultima_actividad: new Date().toISOString(),\n\n etapa: 'ESPERANDO_AUDIO',\n\n audio_count: 0,\n imagenes_count: 0,\n videos_count: 0,\n\n estado: 'ACTIVA',\n ejecucion_id: sessionId,\n motivo_revision: '',\n\n // Nuevos campos para grupo, idioma y clasificación futura\n is_group: isGroup,\n group_jid: groupJid,\n group_name: texto(evento.group_name),\n sender_jid: texto(evento.sender_jid),\n sender_phone: senderPhone,\n sender_name: texto(evento.sender_name),\n\n country_code: texto(evento.country_code),\n pais_detectado: texto(evento.pais_detectado),\n idioma_flujo: texto(evento.idioma_flujo || 'ES'),\n\n tipo_reporte: ''\n },\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -8368, -624 ], "id": "91675542-137d-4976-9c1c-3ba26bb1ddab", "name": "Code - Resolver sesión WhatsApp TEST" }, { "parameters": { "rules": { "values": [ { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "leftValue": "={{ $json.decision_flujo === 'CREAR_SESION' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true }, "id": "acec6506-977a-412d-b08a-fbcc8de4b0b8" } ], "combinator": "and" }, "renameOutput": true, "outputKey": "CREAR_SESION" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "5c9dcfa5-9b15-428c-ad80-c277b0671658", "leftValue": "={{ $json.decision_flujo === 'AVISO_SESION_ACTIVA' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "AVISO_SESION_ACTIVA" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "966402b5-4517-45c2-8a87-0a7aa4e5e682", "leftValue": "={{ $json.decision_flujo === 'SIN_SESION_ACTIVA' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "SIN_SESION_ACTIVA" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "698f3da3-29d2-472f-b625-af387891035e", "leftValue": "={{ $json.decision_flujo === 'CANCELAR_SESION' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "CANCELAR_SESION" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "d5feac06-4493-494e-ac1e-36c007f0de79", "leftValue": "={{ $json.decision_flujo === 'SIN_SESION_PARA_CANCELAR' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "SIN_SESION_PARA_CANCELAR" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "950979b7-b930-4260-a392-52277a742e43", "leftValue": "={{ $json.decision_flujo === 'CONTINUAR_SESION' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "CONTINUAR_SESION" } ] }, "options": {} }, "type": "n8n-nodes-base.switch", "typeVersion": 3.4, "position": [ -8144, -688 ], "id": "50d7bb39-3a6d-4906-b695-e6b09ba5c545", "name": "Switch - Decisión sesión WhatsApp TEST" }, { "parameters": { "jsCode": "const datos = $('Code - Resolver sesión WhatsApp TEST').first().json || {};\n\nconst idioma = String(\n datos.idioma_flujo ||\n datos.nueva_sesion?.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst nombre =\n datos.manager_nombre ||\n datos.sender_name ||\n 'equipo';\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '📸 *GLM Proposal Evidence Registry*',\n '',\n `Hello, ${nombre}.`,\n '',\n 'Use this flow to register evidence for Fulgencio Fumado.',\n '',\n '*Report Type:*',\n '1. *Executed Proposal*: Already implemented.',\n '2. *External Proposal*: Received outside the internal bank.',\n '',\n '*Step 1 of 3 — Voice note*',\n '',\n 'Send a voice note and clearly say if this is an *executed* or *external* proposal.',\n '',\n 'Include:',\n '• Proposal/reference',\n '• Brand/client',\n '• Country',\n '• Location',\n '• Date',\n '• What was implemented or reported',\n '• Results/comments',\n '',\n '*Important:* One proposal/report at a time.'\n ].join('\\n');\n} else {\n mensaje = [\n '📸 *Registro de Evidencias GLM*',\n '',\n `Hola, ${nombre}.`,\n '',\n 'Usa este flujo para registrar evidencias de Fulgencio Fumado.',\n '',\n '*Tipo de Reporte:*',\n '1. *Propuesta Ejecutada*: Ya implementada.',\n '2. *Propuesta Externa*: Recibida fuera del banco interno.',\n '',\n '*Paso 1 de 3 — Nota de voz*',\n '',\n 'Envía una nota de voz e indica claramente si es *ejecutada* o *externa*.',\n '',\n 'Incluye:',\n '• Propuesta/referencia',\n '• Marca/cliente',\n '• País',\n '• Ubicación',\n '• Fecha',\n '• Qué se implementó o reportó',\n '• Resultados/comentarios',\n '',\n '*Importante:* Una sola propuesta o reporte a la vez.'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...datos,\n whatsapp_to: datos.whatsapp_to || datos.group_jid || datos.whatsapp_remote_jid,\n whatsapp_text: mensaje,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5552, -2608 ], "id": "304baef0-fda7-43ce-8bfe-24ed684ced3b", "name": "Code - Preparar bienvenida WhatsApp TEST" }, { "parameters": { "jsCode": "const datos = $json || {};\n\nconst idioma = String(\n datos.idioma_flujo ||\n datos.sesion_activa?.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst etapa = String(datos.sesion_activa?.etapa || '').trim() || 'pendiente';\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '⚠️ You already have an active proposal report in progress.',\n '',\n `Current step: ${etapa}`,\n '',\n 'Please complete that report before starting a new one.',\n '',\n 'To cancel it, use the cancel option.',\n ].join('\\n');\n} else {\n mensaje = [\n '⚠️ Ya tienes un reporte de propuesta en proceso.',\n '',\n `Etapa actual: ${etapa}`,\n '',\n 'Completa ese reporte antes de iniciar uno nuevo.',\n '',\n 'Para cancelarlo, usa la opción de cancelar.',\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...datos,\n whatsapp_to: datos.whatsapp_to || datos.whatsapp_remote_jid,\n whatsapp_text: mensaje,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5520, -2160 ], "id": "bf9701ac-00d2-47c3-a495-ddaad66ed0be", "name": "Code - Preparar aviso sesión activa WhatsApp TEST" }, { "parameters": { "operation": "append", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.nueva_sesion.session_id }}", "manager_telefono": "={{ $json.nueva_sesion.manager_telefono }}", "manager_nombre": "={{ $json.nueva_sesion.manager_nombre }}", "canal_origen": "={{ $json.nueva_sesion.canal_origen }}", "fecha_inicio": "={{ $json.nueva_sesion.fecha_inicio }}", "ultima_actividad": "={{ $json.nueva_sesion.ultima_actividad }}", "etapa": "={{ $json.nueva_sesion.etapa }}", "audio_count": "={{ $json.nueva_sesion.audio_count }}", "imagenes_count": "={{ $json.nueva_sesion.imagenes_count }}", "videos_count": "={{ $json.nueva_sesion.videos_count }}", "estado": "={{ $json.nueva_sesion.estado }}", "ejecucion_id": "={{ $json.nueva_sesion.ejecucion_id }}", "motivo_revision": "={{ $json.nueva_sesion.motivo_revision }}", "is_group": "={{ $json.nueva_sesion.is_group }}", "group_jid": "={{ $json.nueva_sesion.group_jid }}", "group_name": "={{ $json.nueva_sesion.group_name }}", "sender_jid": "={{ $json.nueva_sesion.sender_jid }}", "sender_phone": "={{ $json.nueva_sesion.sender_phone }}", "sender_name": "={{ $json.nueva_sesion.sender_name }}", "country_code": "={{ $json.nueva_sesion.country_code }}", "pais_detectado": "={{ $json.nueva_sesion.pais_detectado }}", "idioma_flujo": "={{ $json.nueva_sesion.idioma_flujo }}", "tipo_reporte": "={{ $json.nueva_sesion.tipo_reporte }}" }, "matchingColumns": [], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "is_group", "displayName": "is_group", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_jid", "displayName": "group_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_name", "displayName": "group_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_jid", "displayName": "sender_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_phone", "displayName": "sender_phone", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_name", "displayName": "sender_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "country_code", "displayName": "country_code", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "pais_detectado", "displayName": "pais_detectado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "idioma_flujo", "displayName": "idioma_flujo", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "tipo_reporte", "displayName": "tipo_reporte", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -5760, -2608 ], "id": "cff69027-2e70-4be6-ad78-80ae6680ca75", "name": "Sheets - Crear sesión WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const datos = $json || {};\n\nconst texto = (valor) => String(valor ?? '').trim();\n\nconst etapa = texto(datos.sesion_activa?.etapa).toUpperCase();\nconst tipoMensaje = texto(datos.message_type).toLowerCase();\nconst accion = texto(datos.accion_flujo).toUpperCase();\n\nconst imagenesCount = Number(datos.sesion_activa?.imagenes_count || 0);\nconst videosCount = Number(datos.sesion_activa?.videos_count || 0);\n\nlet paso_decision = 'NO_DEFINIDO';\n\n// --------------------------------------------------\n// 1. Cancelación global\n// --------------------------------------------------\nif (accion === 'CANCELAR') {\n paso_decision = 'CANCELAR_SESION';\n\n// --------------------------------------------------\n// 2. Paso 1: esperando nota de voz\n// --------------------------------------------------\n} else if (etapa === 'ESPERANDO_AUDIO') {\n if (tipoMensaje === 'audio') {\n paso_decision = 'GUARDAR_AUDIO';\n } else if (tipoMensaje === 'text') {\n paso_decision = 'PEDIR_AUDIO';\n } else {\n paso_decision = 'IGNORAR_EVENTO';\n }\n\n// --------------------------------------------------\n// 3. Paso 2: esperando imágenes obligatorias\n// --------------------------------------------------\n} else if (etapa === 'ESPERANDO_IMAGENES') {\n if (tipoMensaje === 'image') {\n paso_decision = 'GUARDAR_IMAGEN';\n\n } else if (accion === 'FOTOS_LISTAS') {\n // La validación real se hace después leyendo wa_ejecuciones_eventos.\n paso_decision = 'FOTOS_LISTAS';\n\n } else if (tipoMensaje === 'text') {\n paso_decision = 'PEDIR_IMAGENES';\n\n } else {\n // Si WhatsApp manda eventos raros durante imágenes, no responder.\n paso_decision = 'IGNORAR_EVENTO';\n }\n\n// --------------------------------------------------\n// 4. Paso 3: esperando videos opcionales\n// --------------------------------------------------\n} else if (etapa === 'ESPERANDO_VIDEOS') {\n if (tipoMensaje === 'video') {\n paso_decision = 'GUARDAR_VIDEO';\n\n } else if (accion === 'SIN_VIDEO') {\n paso_decision = 'CERRAR_SIN_VIDEO';\n\n } else if (accion === 'LISTO') {\n paso_decision = 'CERRAR_CON_VIDEOS';\n\n } else if (tipoMensaje === 'text') {\n paso_decision = 'PEDIR_VIDEOS';\n\n } else {\n // Si WhatsApp manda eventos raros durante videos, no responder.\n paso_decision = 'IGNORAR_EVENTO';\n }\n\n// --------------------------------------------------\n// 5. Sesión en procesamiento\n// --------------------------------------------------\n} else if (etapa === 'PROCESANDO') {\n paso_decision = 'AVISO_PROCESANDO';\n\n// --------------------------------------------------\n// 6. Etapa no reconocida\n// --------------------------------------------------\n} else {\n paso_decision = 'ETAPA_NO_RECONOCIDA';\n}\n\nreturn [\n {\n json: {\n ...datos,\n\n etapa_actual: etapa,\n tipo_mensaje_actual: tipoMensaje,\n accion_actual: accion,\n\n imagenes_count_actual: imagenesCount,\n videos_count_actual: videosCount,\n\n paso_decision,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -7856, -208 ], "id": "28aaa567-1c39-49ac-802d-38e001d71cc2", "name": "Code - Resolver paso activo WhatsApp TEST" }, { "parameters": { "rules": { "values": [ { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "leftValue": "={{ $json.paso_decision === 'GUARDAR_AUDIO' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true }, "id": "c7fef2d7-50ce-467a-b125-30ae7fba3a58" } ], "combinator": "and" }, "renameOutput": true, "outputKey": "GUARDAR_AUDIO" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "8da042cf-54e6-460f-b505-2b17c4f4ff3c", "leftValue": "={{ $json.paso_decision === 'PEDIR_AUDIO' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "PEDIR_AUDIO" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "ecf52241-ef7e-4a9a-8823-b9e3b438da61", "leftValue": "={{ $json.paso_decision === 'GUARDAR_AUDIO' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "GUARDAR_AUDIO" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "04433553-d0ee-4391-a4f4-926197ac4c0c", "leftValue": "={{ $json.paso_decision === 'PEDIR_AUDIO' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "PEDIR_AUDIO" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "8637dc0f-59b6-40ac-8bc4-baf869829598", "leftValue": "={{ $json.paso_decision === 'GUARDAR_IMAGEN' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "GUARDAR_IMAGEN" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "4096e616-2517-48ab-b6c4-2712f763ec1d", "leftValue": "={{ $json.paso_decision === 'FOTOS_LISTAS' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "FOTOS_LISTAS" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "d1dd1d5c-8e25-4147-876e-5e7227784f6f", "leftValue": "={{ $json.paso_decision === 'PEDIR_IMAGENES' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "PEDIR_IMAGENES" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "bddbb16a-0463-47dd-9a6c-03c3635043f1", "leftValue": "={{ $json.paso_decision === 'GUARDAR_VIDEO' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "GUARDAR_VIDEO" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "fe05ba35-0ccc-417a-a4e8-b907611932e4", "leftValue": "={{ $json.paso_decision === 'CERRAR_SIN_VIDEO' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "CERRAR_SIN_VIDEO" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "94647ac0-ee80-4c96-9f25-8a847704b6a0", "leftValue": "={{ $json.paso_decision === 'CERRAR_CON_VIDEOS' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "CERRAR_CON_VIDEOS" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "3038a656-50ae-4738-b2ff-a6276bd06522", "leftValue": "={{ $json.paso_decision === 'PEDIR_VIDEOS' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "PEDIR_VIDEOS" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "1b20a524-2289-4d37-8372-9a839dadc971", "leftValue": "={{ $json.paso_decision === 'AVISO_PROCESANDO' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "AVISO_PROCESANDO" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "0943bbd0-f5b7-43c4-9b01-7f6bea244894", "leftValue": "={{ $json.paso_decision === 'ETAPA_NO_RECONOCIDA' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "ETAPA_NO_RECONOCIDA" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "84c163a3-3c08-4636-9104-c0e4c55b11e6", "leftValue": "={{ $json.paso_decision === 'IGNORAR_EVENTO' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "IGNORAR_EVENTO" } ] }, "options": {} }, "type": "n8n-nodes-base.switch", "typeVersion": 3.4, "position": [ -7664, 64 ], "id": "b8b395eb-8f3f-4006-8ad8-d426f7e0792a", "name": "Switch - Paso activo WhatsApp TEST" }, { "parameters": { "jsCode": "const datos = $json || {};\n\nconst idioma = String(\n datos.idioma_flujo ||\n datos.sesion_activa?.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '🎙️ I still need the required voice note to start the report.',\n '',\n 'Please send a voice note including:',\n '',\n '• Proposal name or reference',\n '• Brand or client',\n '• Country',\n '• Execution/location details',\n '• Date',\n '• What was implemented or reported',\n '• Any relevant comments or observed results',\n ].join('\\n');\n} else {\n mensaje = [\n '🎙️ Aún necesito la nota de voz obligatoria para iniciar el reporte.',\n '',\n 'Por favor envía una nota de voz indicando:',\n '',\n '• Nombre o referencia de la propuesta',\n '• Marca o cliente',\n '• País',\n '• Ubicación o contexto de la ejecución',\n '• Fecha',\n '• Qué se implementó o qué se está reportando',\n '• Comentarios o resultados observados',\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...datos,\n whatsapp_to: datos.whatsapp_to || datos.whatsapp_remote_jid,\n whatsapp_text: mensaje,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5744, -528 ], "id": "30196ae5-1d82-46e3-9296-32ffa6122ae1", "name": "Code - Preparar solicitud audio WhatsApp TEST" }, { "parameters": { "operation": "append", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 306129743, "mode": "list", "cachedResultName": "wa_ejecuciones_eventos", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=306129743" }, "columns": { "mappingMode": "defineBelow", "value": { "event_id": "={{ $json.event_id }}", "fecha_recepcion": "={{ $json.fecha_recepcion }}", "manager_telefono": "={{ $json.manager_telefono }}", "manager_nombre": "={{ $json.manager_nombre }}", "message_type": "={{ $json.message_type }}", "texto": "={{ $json.texto }}", "comando": "={{ $json.accion_flujo }}", "tiene_media": "={{ $json.tiene_media }}", "media_count": "={{ $json.media_count }}", "raw_preview": "={{ $json.raw_preview }}", "estado": "={{ $json.estado }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "fecha_procesado": "={{ $json.fecha_procesado }}", "session_id": "={{ $json.session_id }}", "canal_origen": "={{ $json.canal_origen }}", "etapa_recibida": "={{ $json.etapa_recibida }}", "media_source_id": "={{ $json.media_source_id }}", "media_mime_type": "={{ $json.media_mime_type }}", "media_file_name": "={{ $json.media_file_name }}", "whatsapp_remote_jid": "={{ $json.whatsapp_remote_jid }}" }, "matchingColumns": [], "schema": [ { "id": "event_id", "displayName": "event_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_recepcion", "displayName": "fecha_recepcion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "message_type", "displayName": "message_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "texto", "displayName": "texto", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "comando", "displayName": "comando", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "tiene_media", "displayName": "tiene_media", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_count", "displayName": "media_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "raw_preview", "displayName": "raw_preview", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_procesado", "displayName": "fecha_procesado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa_recibida", "displayName": "etapa_recibida", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_source_id", "displayName": "media_source_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "media_mime_type", "displayName": "media_mime_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "media_file_name", "displayName": "media_file_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "whatsapp_remote_jid", "displayName": "whatsapp_remote_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -6032, -912 ], "id": "4b7309e9-5602-4c88-b29f-42e44179f445", "name": "Sheets - Guardar evento audio WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const datos = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\nconst sesion = datos.sesion_activa || datos.nueva_sesion || {};\n\nreturn [\n {\n json: {\n ...datos,\n\n session_id: datos.session_id,\n ultima_actividad: new Date().toISOString(),\n\n etapa: 'ESPERANDO_IMAGENES',\n estado: 'ACTIVA',\n\n audio_count: Number(sesion.audio_count || 0) + 1,\n imagenes_count: Number(sesion.imagenes_count || 0),\n videos_count: Number(sesion.videos_count || 0),\n\n manager_telefono: datos.manager_telefono || sesion.manager_telefono || datos.sender_phone || '',\n manager_nombre: datos.manager_nombre || sesion.manager_nombre || datos.sender_name || '',\n\n is_group: datos.is_group ?? sesion.is_group ?? false,\n group_jid: datos.group_jid || sesion.group_jid || '',\n group_name: datos.group_name || sesion.group_name || '',\n sender_jid: datos.sender_jid || sesion.sender_jid || '',\n sender_phone: datos.sender_phone || sesion.sender_phone || datos.manager_telefono || '',\n sender_name: datos.sender_name || sesion.sender_name || datos.manager_nombre || '',\n\n country_code: datos.country_code || sesion.country_code || '',\n pais_detectado: datos.pais_detectado || sesion.pais_detectado || '',\n idioma_flujo: datos.idioma_flujo || sesion.idioma_flujo || 'ES',\n\n tipo_reporte: sesion.tipo_reporte || datos.tipo_reporte || '',\n\n whatsapp_to: datos.whatsapp_to || datos.group_jid || datos.whatsapp_remote_jid || ''\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5824, -912 ], "id": "25599b76-fe73-44b6-80f5-1d8ba9616fd1", "name": "Code - Preparar actualización audio recibido WhatsApp TEST" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ultima_actividad": "={{ $json.ultima_actividad }}", "etapa": "={{ $json.etapa }}", "estado": "={{ $json.estado }}", "audio_count": "={{ $json.audio_count }}", "imagenes_count": "={{ $json.imagenes_count }}", "videos_count": "={{ $json.videos_count }}", "is_group": "={{ $json.is_group }}", "group_jid": "={{ $json.group_jid }}", "group_name": "={{ $json.group_name }}", "sender_jid": "={{ $json.sender_jid }}", "sender_phone": "={{ $json.sender_phone }}", "sender_name": "={{ $json.sender_name }}", "country_code": "={{ $json.country_code }}", "pais_detectado": "={{ $json.pais_detectado }}", "idioma_flujo": "={{ $json.idioma_flujo }}", "tipo_reporte": "={{ $json.tipo_reporte }}", "manager_telefono": "={{ $json.manager_telefono }}", "manager_nombre": "={{ $json.manager_nombre }}", "ejecucion_id": "={{ $json.ejecucion_id }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "is_group", "displayName": "is_group", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_jid", "displayName": "group_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_name", "displayName": "group_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_jid", "displayName": "sender_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_phone", "displayName": "sender_phone", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_name", "displayName": "sender_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "country_code", "displayName": "country_code", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "pais_detectado", "displayName": "pais_detectado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "idioma_flujo", "displayName": "idioma_flujo", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "tipo_reporte", "displayName": "tipo_reporte", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -5616, -912 ], "id": "35b5002d-7c69-4f31-89de-dc5457961a42", "name": "Sheets - Actualizar sesión audio recibido WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const datos = $('Code - Preparar actualización audio recibido WhatsApp TEST').first().json || {};\n\nconst idioma = String(\n datos.idioma_flujo ||\n datos.sesion_activa?.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '✅ Voice note received.',\n '',\n '*Step 2 of 3 — Required images*',\n '',\n 'Now send at least one photo related to the proposal or report.',\n '',\n '⚠️ Important: send images one by one, in separate messages.',\n 'Do not send multiple images together in the same WhatsApp album/message.',\n '',\n 'You can send multiple images, but each one must be sent separately.',\n '',\n 'When you finish sending images, write: PHOTOS READY'\n ].join('\\n');\n} else {\n mensaje = [\n '✅ Nota de voz recibida.',\n '',\n '*Paso 2 de 3 — Imágenes obligatorias*',\n '',\n 'Ahora envía al menos una foto relacionada a la propuesta o reporte.',\n '',\n '⚠️ Importante: envía las imágenes una por una, en mensajes separados.',\n 'No envíes varias imágenes juntas en el mismo álbum/mensaje de WhatsApp.',\n '',\n 'Puedes enviar varias imágenes, pero cada una debe ir por separado.',\n '',\n 'Cuando termines de enviar las imágenes, escribe: FOTOS LISTAS'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...datos,\n whatsapp_to: datos.whatsapp_to || datos.group_jid || datos.whatsapp_remote_jid,\n whatsapp_text: mensaje,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5392, -912 ], "id": "7c1c09aa-4670-47d0-a5a5-3713adf6b6fb", "name": "Code - Preparar solicitud imágenes WhatsApp TEST" }, { "parameters": { "jsCode": "const datos = $json || {};\n\nreturn [\n {\n json: {\n ...datos,\n session_id: datos.session_id,\n ultima_actividad: new Date().toISOString(),\n etapa: 'CANCELADA',\n estado: 'CANCELADA',\n motivo_revision: 'CANCELADO_POR_USUARIO',\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5648, -1344 ], "id": "dd118cad-907f-4bab-89ef-8d75dc2d70cb", "name": "Code - Preparar cancelación sesión WhatsApp TEST" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ultima_actividad": "={{ $json.ultima_actividad }}", "etapa": "={{ $json.etapa }}", "estado": "={{ $json.estado }}", "motivo_revision": "={{ $json.motivo_revision }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -5440, -1344 ], "id": "ac83d38f-7d91-408a-bb43-f8950ccc40a6", "name": "Sheets - Cancelar sesión WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const actual = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst base = getNodeJson('Code - Resolver sesión WhatsApp TEST');\n\nconst sesion =\n base.sesion_activa ||\n actual.sesion_activa ||\n {};\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst idioma = limpiar(\n actual.idioma_flujo ||\n base.idioma_flujo ||\n sesion.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst destinoBase = limpiar(\n base.whatsapp_to ||\n base.group_jid ||\n actual.whatsapp_to ||\n actual.group_jid ||\n base.whatsapp_remote_jid ||\n actual.whatsapp_remote_jid ||\n sesion.group_jid ||\n sesion.whatsapp_to ||\n sesion.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n actual.manager_telefono ||\n base.sender_phone ||\n actual.sender_phone ||\n ''\n);\n\nif (!destinoBase) {\n throw new Error('No se encontró destino WhatsApp para enviar mensaje de cancelación.');\n}\n\nlet whatsappTo = destinoBase;\n\nif (whatsappTo.includes('@g.us')) {\n whatsappTo = whatsappTo;\n} else if (whatsappTo.includes('@s.whatsapp.net')) {\n whatsappTo = whatsappTo;\n} else {\n whatsappTo = `${whatsappTo.replace(/\\D/g, '')}@s.whatsapp.net`;\n}\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '✅ *Report cancelled successfully.*',\n '',\n 'The current report was closed and no additional evidence will be added to that session.',\n '',\n 'When you are ready, you can start a new registration by writing:',\n '',\n '*Hey*'\n ].join('\\n');\n} else {\n mensaje = [\n '✅ *Reporte cancelado correctamente.*',\n '',\n 'El reporte actual fue cerrado y no se agregará más evidencia a esa sesión.',\n '',\n 'Cuando estés listo, puedes iniciar un nuevo registro escribiendo:',\n '',\n '*Hey*'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...base,\n ...actual,\n\n whatsapp_to: whatsappTo,\n whatsapp_text: mensaje\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5184, -1408 ], "id": "119da00c-cea5-4184-90e5-ad4a408271bb", "name": "Code - Preparar mensaje cancelación WhatsApp TEST" }, { "parameters": { "jsCode": "const evento = $json || {};\nconst base = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\nconst sesion = base.sesion_activa || base.nueva_sesion || {};\n\nconst ahora = new Date().toISOString();\n\nconst imagenesActuales = Number(sesion.imagenes_count || 0);\n\nreturn [\n {\n json: {\n ...base,\n ...evento,\n\n session_id: base.session_id || sesion.session_id || evento.session_id || '',\n ejecucion_id: sesion.ejecucion_id || base.ejecucion_id || base.session_id || '',\n\n manager_telefono: base.manager_telefono || sesion.manager_telefono || evento.manager_telefono || base.sender_phone || '',\n manager_nombre: base.manager_nombre || sesion.manager_nombre || evento.manager_nombre || base.sender_name || '',\n\n canal_origen: base.canal_origen || sesion.canal_origen || 'WHATSAPP',\n\n fecha_inicio: sesion.fecha_inicio || base.fecha_inicio || '',\n ultima_actividad: ahora,\n\n etapa: 'ESPERANDO_IMAGENES',\n\n audio_count: Number(sesion.audio_count || 0),\n imagenes_count: imagenesActuales + 1,\n videos_count: Number(sesion.videos_count || 0),\n\n estado: sesion.estado || 'ACTIVA',\n motivo_revision: sesion.motivo_revision || '',\n\n is_group: base.is_group ?? sesion.is_group ?? false,\n group_jid: base.group_jid || sesion.group_jid || '',\n group_name: base.group_name || sesion.group_name || '',\n\n sender_jid: base.sender_jid || sesion.sender_jid || '',\n sender_phone: base.sender_phone || sesion.sender_phone || base.manager_telefono || '',\n sender_name: base.sender_name || sesion.sender_name || base.manager_nombre || '',\n\n country_code: base.country_code || sesion.country_code || '',\n pais_detectado: base.pais_detectado || sesion.pais_detectado || '',\n idioma_flujo: base.idioma_flujo || sesion.idioma_flujo || 'ES',\n\n tipo_reporte: sesion.tipo_reporte || base.tipo_reporte || '',\n\n whatsapp_to: base.whatsapp_to || base.group_jid || base.whatsapp_remote_jid || ''\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -4752, -320 ], "id": "dee83381-1061-405c-978f-33403362f74b", "name": "Code — Preparar actualización imagen recibida" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "estado": "={{ $json.estado }}", "videos_count": "={{ $json.videos_count }}", "imagenes_count": "={{ $json.imagenes_count }}", "audio_count": "={{ $json.audio_count }}", "etapa": "={{ $json.etapa }}", "ultima_actividad": "={{ $json.ultima_actividad }}", "is_group": "={{ $json.is_group }}", "group_jid": "={{ $json.group_jid }}", "group_name": "={{ $json.group_name }}", "sender_jid": "={{ $json.sender_jid }}", "sender_phone": "={{ $json.sender_phone }}", "sender_name": "={{ $json.sender_name }}", "country_code": "={{ $json.country_code }}", "pais_detectado": "={{ $json.pais_detectado }}", "idioma_flujo": "={{ $json.idioma_flujo }}", "tipo_reporte": "={{ $json.tipo_reporte }}", "manager_telefono": "={{ $json.manager_telefono }}", "manager_nombre": "={{ $json.manager_nombre }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "is_group", "displayName": "is_group", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_jid", "displayName": "group_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_name", "displayName": "group_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_jid", "displayName": "sender_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_phone", "displayName": "sender_phone", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_name", "displayName": "sender_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "country_code", "displayName": "country_code", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "pais_detectado", "displayName": "pais_detectado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "idioma_flujo", "displayName": "idioma_flujo", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "tipo_reporte", "displayName": "tipo_reporte", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -4544, -320 ], "id": "392c7a6c-3ab8-4f56-8742-8ab4ffdd5a4c", "name": "Sheets — Actualizar sesión imagen recibida", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const datos = $json || {};\n\nconst idioma = String(\n datos.idioma_flujo ||\n datos.sesion_activa?.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '✅ Image received.',\n '',\n 'You can send more images if needed.',\n '',\n '⚠️ Remember: send each image in a separate message.',\n 'Do not send multiple images together.',\n '',\n 'When you finish sending images, write: PHOTOS READY'\n ].join('\\n');\n} else {\n mensaje = [\n '✅ Imagen recibida.',\n '',\n 'Puedes enviar más imágenes si hace falta.',\n '',\n '⚠️ Recuerda: envía cada imagen en un mensaje separado.',\n 'No envíes varias imágenes juntas.',\n '',\n 'Cuando termines de enviar las imágenes, escribe: FOTOS LISTAS'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...datos,\n whatsapp_to: datos.whatsapp_to || datos.group_jid || datos.whatsapp_remote_jid,\n whatsapp_text: mensaje\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -4336, -320 ], "id": "ebc7dadc-6ddc-4806-8c19-7d4e2ce567cf", "name": "Code — Preparar confirmación imagen" }, { "parameters": { "operation": "append", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 306129743, "mode": "list", "cachedResultName": "wa_ejecuciones_eventos", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=306129743" }, "columns": { "mappingMode": "defineBelow", "value": { "event_id": "={{ $json.event_id }}", "manager_telefono": "={{ $json.manager_telefono }}", "manager_nombre": "={{ $json.manager_nombre }}", "tiene_media": "={{ $json.tiene_media }}", "estado": "=IMAGEN_RECIBIDA", "session_id": "={{ $json.session_id }}", "canal_origen": "=WHATSAPP", "fecha_recepcion": "={{ $json.fecha_recepcion }}", "message_type": "={{ $json.message_type }}", "texto": "={{ $json.texto }}", "media_count": "={{ $json.media_count }}", "raw_preview": "={{ $json.raw_preview }}", "etapa_recibida": "={{ $json.etapa_actual }}", "media_source_id": "={{ $json.media_source_id }}", "media_mime_type": "={{ $json.media_mime_type }}", "media_file_name": "={{ $json.media_file_name }}", "whatsapp_remote_jid": "={{ $json.whatsapp_remote_jid }}", "comando": "={{ $json.accion_flujo }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "fecha_procesado": "={{ new Date().toISOString() }}" }, "matchingColumns": [], "schema": [ { "id": "event_id", "displayName": "event_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_recepcion", "displayName": "fecha_recepcion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "message_type", "displayName": "message_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "texto", "displayName": "texto", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "comando", "displayName": "comando", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "tiene_media", "displayName": "tiene_media", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_count", "displayName": "media_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "raw_preview", "displayName": "raw_preview", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_procesado", "displayName": "fecha_procesado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa_recibida", "displayName": "etapa_recibida", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_source_id", "displayName": "media_source_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "media_mime_type", "displayName": "media_mime_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "media_file_name", "displayName": "media_file_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "whatsapp_remote_jid", "displayName": "whatsapp_remote_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -5024, -320 ], "id": "f8cf4436-cf35-45d6-acc7-7adfb9e3a2dc", "name": "Sheets - Guardar evento imagen WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const actual = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst base = getNodeJson('Code - Resolver paso activo WhatsApp TEST');\nconst sesion = base.sesion_activa || actual.sesion_activa || {};\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst normalizarDestino = (valor) => {\n let raw = limpiar(valor);\n\n if (!raw) return '';\n\n raw = raw.replace('@c.us', '@s.whatsapp.net');\n\n if (raw.includes('@g.us')) return raw;\n\n if (raw.includes('@s.whatsapp.net')) {\n const numero = raw.replace('@s.whatsapp.net', '').replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n }\n\n const numero = raw.replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n};\n\nconst idioma = limpiar(\n actual.idioma_flujo ||\n base.idioma_flujo ||\n sesion.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst destinoBase =\n base.whatsapp_to ||\n base.group_jid ||\n actual.whatsapp_to ||\n actual.group_jid ||\n base.whatsapp_remote_jid ||\n actual.whatsapp_remote_jid ||\n sesion.group_jid ||\n sesion.whatsapp_to ||\n sesion.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n actual.manager_telefono ||\n base.sender_phone ||\n actual.sender_phone ||\n '';\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '📸 I still need at least one image.',\n '',\n 'The voice note was already received, but one evidence photo is required to complete the report.',\n '',\n '⚠️ Please send at least one image, one per message.',\n 'Do not send multiple images together.',\n '',\n 'When you finish sending images, write: PHOTOS READY'\n ].join('\\n');\n} else {\n mensaje = [\n '📸 Necesito que me envíes al menos una imagen.',\n '',\n 'La nota de voz ya fue recibida, pero para completar el reporte hace falta una foto de evidencia.',\n '',\n '⚠️ Por favor envía al menos una imagen, una por mensaje.',\n 'No envíes varias imágenes juntas.',\n '',\n 'Cuando termines de enviar las imágenes, escribe: FOTOS LISTAS'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...base,\n ...actual,\n\n whatsapp_to: normalizarDestino(destinoBase),\n whatsapp_text: mensaje\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5456, 208 ], "id": "75998ea0-d2ec-4614-b18e-824019627d72", "name": "Code - Preparar aviso falta imagen WhatsApp TEST" }, { "parameters": { "jsCode": "const base = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\nconst sesion = base.sesion_activa || base.nueva_sesion || {};\n\nconst rows = $input.all().map(item => item.json || {});\n\nconst texto = (valor) => String(valor ?? '').trim();\n\nconst normalizarNumero = (valor) =>\n texto(valor)\n .replace('@s.whatsapp.net', '')\n .replace('@c.us', '')\n .replace('@g.us', '')\n .replace('@lid', '')\n .replace(/\\D/g, '');\n\nconst sessionId = texto(base.session_id || sesion.session_id);\nconst ejecucionId = texto(sesion.ejecucion_id || base.ejecucion_id || sessionId);\n\nconst ahora = new Date().toISOString();\n\nconst fechaInicioSesion = texto(\n sesion.fecha_inicio ||\n base.fecha_inicio ||\n base.sesion_activa?.fecha_inicio ||\n ''\n);\n\nconst fechaInicioMs = fechaInicioSesion\n ? new Date(fechaInicioSesion).getTime()\n : 0;\n\nconst fechaAhoraMs = Date.now();\n\nconst groupJid = texto(\n base.group_jid ||\n sesion.group_jid ||\n base.whatsapp_remote_jid ||\n ''\n);\n\nconst managerTelefono = normalizarNumero(\n base.manager_telefono ||\n base.sender_phone ||\n sesion.manager_telefono ||\n sesion.sender_phone ||\n ''\n);\n\n// Ventana máxima de seguridad para esta sesión.\n// Así no mezclamos fotos viejas del mismo grupo/persona.\n// 45 minutos es suficiente para una ejecución normal.\nconst ventanaMaximaMs = 45 * 60 * 1000;\n\nconst imagenesUnicas = [];\nconst vistos = new Set();\n\nfor (const row of rows) {\n const tipo = texto(row.message_type).toLowerCase();\n\n if (tipo !== 'image') continue;\n\n const rowSessionId = texto(row.session_id);\n const rowGroupJid = texto(row.whatsapp_remote_jid);\n const rowManager = normalizarNumero(row.manager_telefono || row.sender_phone || '');\n\n const rowFecha = texto(row.fecha_recepcion || row.fecha_procesado || '');\n const rowFechaMs = rowFecha ? new Date(rowFecha).getTime() : 0;\n\n const mismaSesion = sessionId && rowSessionId === sessionId;\n\n const mismoGrupoYManager =\n groupJid &&\n rowGroupJid === groupJid &&\n managerTelefono &&\n rowManager === managerTelefono;\n\n const dentroDeVentana =\n rowFechaMs &&\n fechaInicioMs &&\n rowFechaMs >= fechaInicioMs - 15000 &&\n rowFechaMs <= fechaAhoraMs + 15000 &&\n rowFechaMs - fechaInicioMs <= ventanaMaximaMs;\n\n const perteneceALaEjecucion =\n mismaSesion ||\n (mismoGrupoYManager && dentroDeVentana);\n\n if (!perteneceALaEjecucion) continue;\n\n const mediaId = texto(row.media_source_id || row.event_id);\n\n if (!mediaId) continue;\n if (vistos.has(mediaId)) continue;\n\n vistos.add(mediaId);\n imagenesUnicas.push(row);\n}\n\nconst countSesion = Number(sesion.imagenes_count || base.imagenes_count_actual || 0);\n\nconst imagenesCountReal = imagenesUnicas.length > 0\n ? imagenesUnicas.length\n : countSesion;\n\nconst idsImagenes = imagenesUnicas\n .map(row => texto(row.media_source_id || row.event_id))\n .filter(Boolean);\n\nreturn [\n {\n json: {\n ...base,\n\n session_id: sessionId,\n ejecucion_id: ejecucionId,\n\n ultima_actividad: ahora,\n\n etapa: 'ESPERANDO_VIDEOS',\n estado: 'ACTIVA',\n\n audio_count: Number(sesion.audio_count || 0),\n imagenes_count: imagenesCountReal,\n videos_count: Number(sesion.videos_count || 0),\n\n manager_telefono: base.manager_telefono || sesion.manager_telefono || base.sender_phone || '',\n manager_nombre: base.manager_nombre || sesion.manager_nombre || base.sender_name || '',\n\n is_group: base.is_group ?? sesion.is_group ?? false,\n group_jid: base.group_jid || sesion.group_jid || '',\n group_name: base.group_name || sesion.group_name || '',\n\n sender_jid: base.sender_jid || sesion.sender_jid || '',\n sender_phone: base.sender_phone || sesion.sender_phone || base.manager_telefono || '',\n sender_name: base.sender_name || sesion.sender_name || base.manager_nombre || '',\n\n country_code: base.country_code || sesion.country_code || '',\n pais_detectado: base.pais_detectado || sesion.pais_detectado || '',\n idioma_flujo: base.idioma_flujo || sesion.idioma_flujo || 'ES',\n\n tipo_reporte: sesion.tipo_reporte || base.tipo_reporte || '',\n\n whatsapp_to: base.whatsapp_to || base.group_jid || base.whatsapp_remote_jid || '',\n\n imagenes_eventos_detectados: imagenesUnicas.length,\n imagenes_media_source_ids: idsImagenes.join(','),\n\n debug_fotos_listas: {\n session_id_actual: sessionId,\n fecha_inicio_sesion: fechaInicioSesion,\n group_jid: groupJid,\n manager_telefono: managerTelefono,\n total_rows_leidas: rows.length,\n total_imagenes_detectadas: imagenesUnicas.length,\n ids_imagenes: idsImagenes\n }\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5120, -16 ], "id": "b0e3e960-8cc9-453e-bbf2-4e410ab4a178", "name": "Preparar actualización fotos listas" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ultima_actividad": "={{ $json.ultima_actividad }}", "etapa": "={{ $json.etapa }}", "audio_count": "={{ $json.audio_count }}", "imagenes_count": "={{ $json.imagenes_count }}", "videos_count": "={{ $json.videos_count }}", "estado": "={{ $json.estado }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "motivo_revision": "={{ $json.motivo_revision }}", "is_group": "={{ $json.is_group }}", "group_jid": "={{ $json.group_jid }}", "group_name": "={{ $json.group_name }}", "sender_jid": "={{ $json.sender_jid }}", "sender_phone": "={{ $json.sender_phone }}", "sender_name": "={{ $json.sender_name }}", "country_code": "={{ $json.country_code }}", "pais_detectado": "={{ $json.pais_detectado }}", "idioma_flujo": "={{ $json.idioma_flujo }}", "tipo_reporte": "={{ $json.tipo_reporte }}", "manager_telefono": "={{ $json.manager_telefono }}", "manager_nombre": "={{ $json.manager_nombre }}", "canal_origen": "={{ $json.canal_origen }}", "fecha_inicio": "={{ $json.fecha_inicio }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "is_group", "displayName": "is_group", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_jid", "displayName": "group_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_name", "displayName": "group_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_jid", "displayName": "sender_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_phone", "displayName": "sender_phone", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_name", "displayName": "sender_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "country_code", "displayName": "country_code", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "pais_detectado", "displayName": "pais_detectado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "idioma_flujo", "displayName": "idioma_flujo", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "tipo_reporte", "displayName": "tipo_reporte", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -4928, -16 ], "id": "dea8b556-e019-40b3-97fc-04e1a47c1ed6", "name": "Sheets - Actualizar sesión fotos listas WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const actual = $json || {};\nconst base = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\nconst sesion = base.sesion_activa || actual.sesion_activa || {};\n\nconst idioma = String(\n actual.idioma_flujo ||\n base.idioma_flujo ||\n sesion.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst destinoBase = String(\n base.whatsapp_to ||\n base.group_jid ||\n actual.whatsapp_to ||\n actual.group_jid ||\n base.whatsapp_remote_jid ||\n actual.whatsapp_remote_jid ||\n sesion.group_jid ||\n sesion.whatsapp_to ||\n sesion.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n actual.manager_telefono ||\n ''\n).trim();\n\nif (!destinoBase) {\n throw new Error('No se encontró destino WhatsApp para enviar solicitud de videos.');\n}\n\nlet whatsappTo = destinoBase;\n\nif (whatsappTo.includes('@g.us')) {\n whatsappTo = whatsappTo;\n} else if (whatsappTo.includes('@s.whatsapp.net')) {\n whatsappTo = whatsappTo;\n} else {\n whatsappTo = `${whatsappTo.replace(/\\D/g, '')}@s.whatsapp.net`;\n}\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '*Step 3 of 3 — Optional videos*',\n '',\n 'You can send one or more videos of the execution if you have them.',\n '',\n '⚠️ Important: send videos one by one, in separate messages.',\n 'Do not send multiple videos together.',\n '',\n 'If you do not have videos, write: NO VIDEO',\n '',\n 'If you already sent videos and finished, write: DONE',\n ].join('\\n');\n} else {\n mensaje = [\n '*Paso 3 de 3 — Videos opcionales*',\n '',\n 'Puedes enviar uno o varios videos de la ejecución si tienes.',\n '',\n '⚠️ Importante: envía los videos uno por uno, en mensajes separados.',\n 'No envíes varios videos juntos.',\n '',\n 'Si no tienes videos, escribe: SIN VIDEO',\n '',\n 'Si enviaste videos y ya terminaste, escribe: LISTO',\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...base,\n ...actual,\n\n session_id: actual.session_id || base.session_id || sesion.session_id || '',\n ejecucion_id: actual.ejecucion_id || base.ejecucion_id || sesion.ejecucion_id || '',\n\n manager_telefono: actual.manager_telefono || base.manager_telefono || sesion.manager_telefono || '',\n manager_nombre: actual.manager_nombre || base.manager_nombre || sesion.manager_nombre || '',\n\n is_group: actual.is_group ?? base.is_group ?? sesion.is_group ?? false,\n group_jid: actual.group_jid || base.group_jid || sesion.group_jid || '',\n sender_phone: actual.sender_phone || base.sender_phone || sesion.sender_phone || '',\n idioma_flujo: idioma,\n\n whatsapp_to: whatsappTo,\n whatsapp_text: mensaje,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -4704, -16 ], "id": "50b9bdba-c99b-4150-9a85-e57c18e24f5a", "name": "Code - Preparar solicitud videos WhatsApp TEST" }, { "parameters": { "jsCode": "const actual = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst base = getNodeJson('Code - Resolver paso activo WhatsApp TEST');\nconst sesion = base.sesion_activa || actual.sesion_activa || {};\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst normalizarDestino = (valor) => {\n let raw = limpiar(valor);\n\n if (!raw) return '';\n\n raw = raw.replace('@c.us', '@s.whatsapp.net');\n\n if (raw.includes('@g.us')) return raw;\n\n if (raw.includes('@s.whatsapp.net')) {\n const numero = raw.replace('@s.whatsapp.net', '').replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n }\n\n const numero = raw.replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n};\n\nconst idioma = limpiar(\n actual.idioma_flujo ||\n base.idioma_flujo ||\n sesion.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst destinoBase =\n base.whatsapp_to ||\n base.group_jid ||\n actual.whatsapp_to ||\n actual.group_jid ||\n base.whatsapp_remote_jid ||\n actual.whatsapp_remote_jid ||\n sesion.group_jid ||\n sesion.whatsapp_to ||\n sesion.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n actual.manager_telefono ||\n base.sender_phone ||\n actual.sender_phone ||\n '';\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '🎥 We are already on the optional videos step.',\n '',\n 'You can send one or more videos of the execution.',\n '',\n '⚠️ Remember: send each video in a separate message.',\n 'Do not send multiple videos together.',\n '',\n 'If you do not have videos, write: *NO VIDEO*',\n '',\n 'If you already sent videos and finished, write: *DONE*'\n ].join('\\n');\n} else {\n mensaje = [\n '🎥 Ya estamos en el paso de videos opcionales.',\n '',\n 'Puedes enviar uno o varios videos de la ejecución.',\n '',\n '⚠️ Recuerda: envía cada video en un mensaje separado.',\n 'No envíes varios videos juntos.',\n '',\n 'Si no tienes videos, escribe: *SIN VIDEO*',\n '',\n 'Si ya enviaste videos y terminaste, escribe: *LISTO*'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...base,\n ...actual,\n\n whatsapp_to: normalizarDestino(destinoBase),\n whatsapp_text: mensaje\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5008, 1520 ], "id": "08b3a87f-00c6-439b-bcbe-131d86d100d5", "name": "Code - Preparar recordatorio videos WhatsApp TEST" }, { "parameters": { "jsCode": "const base = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\nconst sesion = base.sesion_activa || {};\n\nconst ahora = new Date().toISOString();\n\nconst telefonoBase = String(\n base.whatsapp_to ||\n base.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n ''\n).trim();\n\nconst whatsappTo = telefonoBase.includes('@s.whatsapp.net')\n ? telefonoBase\n : `${telefonoBase.replace(/\\D/g, '')}@s.whatsapp.net`;\n\nreturn [\n {\n json: {\n ...base,\n\n session_id: base.session_id || sesion.session_id || '',\n manager_telefono: sesion.manager_telefono || base.manager_telefono || '',\n manager_nombre: sesion.manager_nombre || base.manager_nombre || '',\n canal_origen: sesion.canal_origen || base.canal_origen || 'WHATSAPP',\n\n fecha_inicio: sesion.fecha_inicio || '',\n ultima_actividad: ahora,\n\n etapa: 'PROCESANDO',\n estado: 'LISTO_PARA_RECUPERAR_MEDIA',\n\n audio_count: Number(sesion.audio_count || 0),\n imagenes_count: Number(sesion.imagenes_count || 0),\n videos_count: Number(sesion.videos_count || 0),\n\n ejecucion_id: sesion.ejecucion_id || base.ejecucion_id || base.session_id || '',\n motivo_revision: 'SIN_VIDEO_REPORTADO',\n\n whatsapp_to: whatsappTo\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -6432, 512 ], "id": "c4db0aed-c18e-429c-a6ff-299bd94d2797", "name": "CERRAR_SIN_VIDEO" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ultima_actividad": "={{ $json.ultima_actividad }}", "etapa": "={{ $json.etapa }}", "audio_count": "={{ $json.audio_count }}", "imagenes_count": "={{ $json.imagenes_count }}", "videos_count": "={{ $json.videos_count }}", "estado": "={{ $json.estado }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "motivo_revision": "={{ $json.motivo_revision }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -6224, 512 ], "id": "27195f01-98fa-4772-b0c6-d82b8241cc83", "name": "Sheets - Actualizar sesión cierre sin video WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "operation": "append", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 306129743, "mode": "list", "cachedResultName": "wa_ejecuciones_eventos", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=306129743" }, "columns": { "mappingMode": "defineBelow", "value": { "event_id": "={{ $json.event_id }}", "fecha_recepcion": "={{ $json.fecha_recepcion }}", "manager_telefono": "={{ $json.manager_telefono }}", "manager_nombre": "={{ $json.manager_nombre }}", "message_type": "={{ $json.message_type }}", "texto": "={{ $json.texto }}", "comando": "={{ $json.accion_flujo }}", "tiene_media": "={{ $json.tiene_media }}", "media_count": "={{ $json.media_count }}", "raw_preview": "={{ $json.raw_preview }}", "estado": "=VIDEO_RECIBIDO", "ejecucion_id": "={{ $json.ejecucion_id }}", "session_id": "={{ $json.session_id }}", "canal_origen": "={{ $json.canal_origen }}", "etapa_recibida": "={{ $json.etapa_actual }}", "media_source_id": "={{ $json.media_source_id }}", "media_mime_type": "={{ $json.media_mime_type }}", "media_file_name": "={{ $json.media_file_name }}", "whatsapp_remote_jid": "={{ $json.whatsapp_remote_jid }}" }, "matchingColumns": [], "schema": [ { "id": "event_id", "displayName": "event_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_recepcion", "displayName": "fecha_recepcion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "message_type", "displayName": "message_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "texto", "displayName": "texto", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "comando", "displayName": "comando", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "tiene_media", "displayName": "tiene_media", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_count", "displayName": "media_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "raw_preview", "displayName": "raw_preview", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_procesado", "displayName": "fecha_procesado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa_recibida", "displayName": "etapa_recibida", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_source_id", "displayName": "media_source_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "media_mime_type", "displayName": "media_mime_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "media_file_name", "displayName": "media_file_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "whatsapp_remote_jid", "displayName": "whatsapp_remote_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -5312, 384 ], "id": "c98b3d5e-cf77-46c6-9d5e-2b6222949656", "name": "Sheets - Guardar evento video WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const evento = $json || {};\nconst base = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\nconst sesion = base.sesion_activa || {};\n\nconst ahora = new Date().toISOString();\n\nconst videosActuales = Number(sesion.videos_count || 0);\n\nconst telefonoBase = String(\n base.whatsapp_to ||\n base.whatsapp_remote_jid ||\n evento.whatsapp_to ||\n evento.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n ''\n).trim();\n\nconst whatsappTo = telefonoBase.includes('@s.whatsapp.net')\n ? telefonoBase\n : `${telefonoBase.replace(/\\D/g, '')}@s.whatsapp.net`;\n\nreturn [\n {\n json: {\n ...base,\n ...evento,\n\n session_id: base.session_id || sesion.session_id || evento.session_id || '',\n manager_telefono: sesion.manager_telefono || base.manager_telefono || evento.manager_telefono || '',\n manager_nombre: sesion.manager_nombre || base.manager_nombre || evento.manager_nombre || '',\n canal_origen: sesion.canal_origen || base.canal_origen || 'WHATSAPP',\n\n fecha_inicio: sesion.fecha_inicio || '',\n ultima_actividad: ahora,\n\n // Seguimos en videos hasta que el usuario escriba LISTO\n etapa: 'ESPERANDO_VIDEOS',\n\n audio_count: Number(sesion.audio_count || 0),\n imagenes_count: Number(sesion.imagenes_count || 0),\n videos_count: videosActuales + 1,\n\n estado: sesion.estado || 'ACTIVA',\n ejecucion_id: sesion.ejecucion_id || base.ejecucion_id || base.session_id || '',\n motivo_revision: sesion.motivo_revision || '',\n\n whatsapp_to: whatsappTo\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5104, 384 ], "id": "7bc3bb63-803e-43e9-9df7-1ed53a7597cb", "name": "Code - Preparar actualización video recibido WhatsApp TEST" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "motivo_revision": "={{ $json.motivo_revision }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "estado": "={{ $json.estado }}", "videos_count": "={{ $json.videos_count }}", "imagenes_count": "={{ $json.imagenes_count }}", "audio_count": "={{ $json.audio_count }}", "etapa": "={{ $json.etapa }}", "ultima_actividad": "={{ $json.ultima_actividad }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -4896, 384 ], "id": "27556223-2f6f-464f-819a-a035a66e3aad", "name": "Sheets - Actualizar sesión video recibido WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const actual = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst base = getNodeJson('Code - Resolver paso activo WhatsApp TEST');\nconst sesion = base.sesion_activa || actual.sesion_activa || {};\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst normalizarDestino = (valor) => {\n let raw = limpiar(valor);\n\n if (!raw) return '';\n\n raw = raw.replace('@c.us', '@s.whatsapp.net');\n\n if (raw.includes('@g.us')) return raw;\n\n if (raw.includes('@s.whatsapp.net')) {\n const numero = raw.replace('@s.whatsapp.net', '').replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n }\n\n const numero = raw.replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n};\n\nconst idioma = limpiar(\n actual.idioma_flujo ||\n base.idioma_flujo ||\n sesion.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst destinoBase =\n actual.whatsapp_to ||\n actual.group_jid ||\n actual.whatsapp_remote_jid ||\n base.whatsapp_to ||\n base.group_jid ||\n base.whatsapp_remote_jid ||\n sesion.group_jid ||\n sesion.whatsapp_to ||\n sesion.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n actual.manager_telefono ||\n base.sender_phone ||\n actual.sender_phone ||\n '';\n\nif (!destinoBase) {\n throw new Error('No se encontró destino WhatsApp para enviar confirmación de video.');\n}\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '✅ Video received.',\n '',\n 'You can send more videos if needed.',\n '',\n '⚠️ Remember: send each video in a separate message.',\n 'Do not send multiple videos together.',\n '',\n 'When you finish sending videos, write: DONE',\n '',\n 'If you do not have more videos, you can also write: DONE'\n ].join('\\n');\n} else {\n mensaje = [\n '✅ Video recibido.',\n '',\n 'Puedes enviar más videos si hace falta.',\n '',\n '⚠️ Recuerda: envía cada video en un mensaje separado.',\n 'No envíes varios videos juntos.',\n '',\n 'Cuando termines de enviar los videos, escribe: LISTO',\n '',\n 'Si no tienes más videos, también puedes escribir: LISTO'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...base,\n ...actual,\n\n whatsapp_to: normalizarDestino(destinoBase),\n whatsapp_text: mensaje\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -4688, 384 ], "id": "ce363661-867f-4c31-b730-a7aaf88453cc", "name": "Code - Preparar confirmación video WhatsApp TEST" }, { "parameters": { "jsCode": "const base = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\nconst sesion = base.sesion_activa || {};\n\nconst ahora = new Date().toISOString();\n\nconst telefonoBase = String(\n base.whatsapp_to ||\n base.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n ''\n).trim();\n\nconst whatsappTo = telefonoBase.includes('@s.whatsapp.net')\n ? telefonoBase\n : `${telefonoBase.replace(/\\D/g, '')}@s.whatsapp.net`;\n\nreturn [\n {\n json: {\n ...base,\n\n session_id: base.session_id || sesion.session_id || '',\n manager_telefono: sesion.manager_telefono || base.manager_telefono || '',\n manager_nombre: sesion.manager_nombre || base.manager_nombre || '',\n canal_origen: sesion.canal_origen || base.canal_origen || 'WHATSAPP',\n\n fecha_inicio: sesion.fecha_inicio || '',\n ultima_actividad: ahora,\n\n etapa: 'PROCESANDO',\n estado: 'LISTO_PARA_RECUPERAR_MEDIA',\n\n audio_count: Number(sesion.audio_count || 0),\n imagenes_count: Number(sesion.imagenes_count || 0),\n videos_count: Number(sesion.videos_count || 0),\n\n ejecucion_id: sesion.ejecucion_id || base.ejecucion_id || base.session_id || '',\n motivo_revision: 'CON_VIDEO_REPORTADO',\n\n whatsapp_to: whatsappTo\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -6512, 800 ], "id": "b2d8579c-f7b8-486a-a502-3b0e91912628", "name": "Code - Preparar cierre con videos WhatsApp TEST" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ultima_actividad": "={{ $json.ultima_actividad }}", "etapa": "={{ $json.etapa }}", "audio_count": "={{ $json.audio_count }}", "imagenes_count": "={{ $json.imagenes_count }}", "videos_count": "={{ $json.videos_count }}", "estado": "={{ $json.estado }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "motivo_revision": "={{ $json.motivo_revision }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -6304, 800 ], "id": "ccb46bd5-6e01-42d5-89ab-54e4622265be", "name": "Sheets - Actualizar sesión cierre con videos WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const data = $json || {};\nconst base = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\nconst sesion = base.sesion_activa || {};\n\nconst telefonoBase = String(\n base.whatsapp_to ||\n base.whatsapp_remote_jid ||\n data.whatsapp_to ||\n data.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n data.manager_telefono ||\n base.manager_telefono ||\n ''\n).trim();\n\nconst whatsappTo = telefonoBase.includes('@s.whatsapp.net')\n ? telefonoBase\n : `${telefonoBase.replace(/\\D/g, '')}@s.whatsapp.net`;\n\nreturn [\n {\n json: {\n ...base,\n ...data,\n\n whatsapp_to: whatsappTo,\n\n whatsapp_text: `⏳ Tu reporte ya fue recibido y está pendiente de procesamiento.\n\nPor favor espera mientras se prepara el análisis de la evidencia.`\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -4944, 1760 ], "id": "b5960269-5fca-4d10-b2ef-eda72eb68b10", "name": "Code - Preparar aviso procesando WhatsApp TEST" }, { "parameters": { "jsCode": "const data = $json || {};\n\nreturn [\n {\n json: {\n ...data,\n\n session_id_busqueda: data.session_id || '',\n estado_procesamiento: 'BUSCAR_EVENTOS_MEDIA'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -4720, 928 ], "id": "6cee8abe-abb5-4818-ac8c-1f49f0905bcc", "name": "Code - Preparar búsqueda eventos media WhatsApp TEST" }, { "parameters": { "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 306129743, "mode": "list", "cachedResultName": "wa_ejecuciones_eventos", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=306129743" }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -4464, 928 ], "id": "e49fc551-3ddb-4041-88fd-c4329ef64d54", "name": "Sheets - Leer eventos WhatsApp TEST", "alwaysOutputData": true, "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const cierre = $('Code - Preparar búsqueda eventos media WhatsApp TEST').first().json || {};\nconst sessionId = String(cierre.session_id_busqueda || cierre.session_id || '').trim();\n\nconst eventos = $input.all().map(item => item.json || {});\n\nconst eventosSesion = eventos.filter(evento => {\n return String(evento.session_id || '').trim() === sessionId;\n});\n\nconst eventosMedia = eventosSesion.filter(evento => {\n const tipo = String(evento.message_type || '').toLowerCase();\n return ['audio', 'image', 'video'].includes(tipo);\n});\n\nconst audios = eventosMedia.filter(e => String(e.message_type || '').toLowerCase() === 'audio');\nconst imagenes = eventosMedia.filter(e => String(e.message_type || '').toLowerCase() === 'image');\nconst videos = eventosMedia.filter(e => String(e.message_type || '').toLowerCase() === 'video');\n\nreturn [\n {\n json: {\n ...cierre,\n\n total_eventos_sesion: eventosSesion.length,\n total_media: eventosMedia.length,\n\n audio_count_real: audios.length,\n imagenes_count_real: imagenes.length,\n videos_count_real: videos.length,\n\n eventos_audio: audios,\n eventos_imagenes: imagenes,\n eventos_videos: videos,\n\n media_lista: eventosMedia,\n\n estado_procesamiento: 'EVENTOS_MEDIA_FILTRADOS'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -4224, 928 ], "id": "6c7e217e-6200-46d8-892a-adc9b273e4a7", "name": "Code - Filtrar eventos media de sesión WhatsApp TEST" }, { "parameters": { "jsCode": "const data = $json || {};\nconst mediaLista = Array.isArray(data.media_lista) ? data.media_lista : [];\n\nconst sessionId = String(data.session_id || data.session_id_busqueda || '').trim();\nconst ejecucionId = String(data.ejecucion_id || sessionId).trim();\n\nconst items = mediaLista.map((media, index) => {\n const tipo = String(media.message_type || '').toLowerCase();\n const mime = String(media.media_mime_type || '').trim();\n\n let extension = 'bin';\n\n if (mime.includes('ogg')) extension = 'ogg';\n else if (mime.includes('mpeg')) extension = 'mp3';\n else if (mime.includes('mp4')) extension = 'mp4';\n else if (mime.includes('jpeg') || mime.includes('jpg')) extension = 'jpg';\n else if (mime.includes('png')) extension = 'png';\n else if (tipo === 'audio') extension = 'ogg';\n else if (tipo === 'image') extension = 'jpg';\n else if (tipo === 'video') extension = 'mp4';\n\n const mediaSourceId = String(\n media.media_source_id ||\n media.event_id ||\n ''\n ).trim();\n\n const remoteJid = String(\n media.whatsapp_remote_jid ||\n data.whatsapp_remote_jid ||\n data.whatsapp_to ||\n media.manager_telefono ||\n data.manager_telefono ||\n ''\n ).trim();\n\n return {\n json: {\n ...data,\n\n media_index: index + 1,\n media_total: mediaLista.length,\n\n session_id: sessionId,\n ejecucion_id: ejecucionId,\n\n media_event_id: media.event_id || '',\n media_source_id: mediaSourceId,\n media_type: tipo,\n media_mime_type: mime,\n media_extension: extension,\n\n whatsapp_remote_jid: remoteJid,\n\n media_file_name: `${ejecucionId}_${tipo}_${index + 1}.${extension}`,\n\n evento_original: media,\n\n estado_procesamiento: 'MEDIA_ITEM_PREPARADO'\n }\n };\n});\n\nreturn items;" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -3984, 928 ], "id": "6d74dd89-9530-44d7-ae69-2e771bf8aab9", "name": "Code - Separar media en items WhatsApp TEST" }, { "parameters": { "method": "POST", "url": "https://wsp.gomezleemarketing.com/chat/getBase64FromMediaMessage/botsoporte", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "apikey", "value": "={{'C267126ABB45-4C12-B626-6BAB1833F5D7'}}" }, { "name": "Content-Type", "value": "application/json" } ] }, "sendBody": true, "specifyBody": "json", "jsonBody": "={\n \"message\": {\n \"key\": {\n \"id\": \"{{ $json.media_source_id }}\"\n }\n },\n \"convertToMp4\": true\n}", "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ -3776, 928 ], "id": "6c936089-6193-479f-b87b-a98e69e4fa92", "name": "HTTP Request - Obtener media base64 Evolution TEST", "alwaysOutputData": true }, { "parameters": { "jsCode": "const httpItems = $input.all();\nconst mediaItems = $('Code - Separar media en items WhatsApp TEST').all();\n\nconst buscarBase64 = (obj) => {\n if (!obj || typeof obj !== 'object') return '';\n\n if (typeof obj.base64 === 'string') return obj.base64;\n if (typeof obj.data === 'string') return obj.data;\n if (typeof obj.media === 'string') return obj.media;\n\n if (obj.message && typeof obj.message.base64 === 'string') return obj.message.base64;\n if (obj.response && typeof obj.response.base64 === 'string') return obj.response.base64;\n if (obj.data && typeof obj.data.base64 === 'string') return obj.data.base64;\n\n return '';\n};\n\nreturn httpItems.map((item, index) => {\n const respuesta = item.json || {};\n const mediaItem = mediaItems[index]?.json || mediaItems[0]?.json || {};\n\n let base64 = buscarBase64(respuesta);\n\n base64 = String(base64 || '').trim();\n base64 = base64.replace(/^data:.*?;base64,/, '');\n\n if (!base64) {\n throw new Error(`No se encontró base64 para media_source_id: ${mediaItem.media_source_id || 'SIN_ID'}`);\n }\n\n return {\n json: {\n ...mediaItem,\n\n base64_length: base64.length,\n estado_procesamiento: 'MEDIA_BASE64_RECUPERADA'\n },\n binary: {\n data: {\n data: base64,\n mimeType: mediaItem.media_mime_type || 'application/octet-stream',\n fileName: mediaItem.media_file_name || 'media_whatsapp.bin'\n }\n }\n };\n});" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -3568, 928 ], "id": "73f2d7f0-bc17-47cc-9b7a-f477f942bf63", "name": "Code - Convertir base64 a binario WhatsApp TEST" }, { "parameters": { "name": "={{ $json.media_file_name }}", "driveId": { "__rl": true, "mode": "list", "value": "My Drive" }, "folderId": { "__rl": true, "value": "=1G11WZnOwPp7UC2moPE6rEvlPFl7_OoSB", "mode": "id" }, "options": {} }, "type": "n8n-nodes-base.googleDrive", "typeVersion": 3, "position": [ -3328, 928 ], "id": "c9d49cee-9f7f-4dfe-bf4a-5729a3f64298", "name": "Drive - Subir media WhatsApp TEST", "credentials": { "googleDriveOAuth2Api": { "id": "g23xdGLZRzBGqKgH", "name": "Isaac - Google Drive" } } }, { "parameters": { "jsCode": "const driveItems = $input.all();\nconst mediaItems = $('Code - Convertir base64 a binario WhatsApp TEST').all();\n\nreturn driveItems.map((item, index) => {\n const drive = item.json || {};\n const media = mediaItems[index]?.json || mediaItems[0]?.json || {};\n\n const fileId =\n drive.id ||\n drive.fileId ||\n drive.file_id ||\n '';\n\n const webViewLink =\n drive.webViewLink ||\n drive.webContentLink ||\n (fileId ? `https://drive.google.com/file/d/${fileId}/view` : '');\n\n return {\n json: {\n ...media,\n\n drive_file_id: fileId,\n drive_file_name: media.media_file_name,\n drive_file_url: webViewLink,\n\n estado_procesamiento: 'MEDIA_GUARDADA_EN_DRIVE'\n }\n };\n});" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -3120, 928 ], "id": "4bfe263f-272f-4738-8664-4f60990ad359", "name": "Code - Preparar registro media Drive WhatsApp TEST" }, { "parameters": { "jsCode": "const mediaItems = $('Code - Preparar registro media Drive WhatsApp TEST').all();\n\nconst media = mediaItems.map(item => item.json || {});\n\nconst base = media[0] || {};\n\nconst audios = media.filter(m => String(m.media_type || '').toLowerCase() === 'audio');\nconst imagenes = media.filter(m => String(m.media_type || '').toLowerCase() === 'image');\nconst videos = media.filter(m => String(m.media_type || '').toLowerCase() === 'video');\n\nreturn [\n {\n json: {\n ...base,\n\n session_id: base.session_id || '',\n ejecucion_id: base.ejecucion_id || base.session_id || '',\n\n audio_drive_urls: audios.map(m => m.drive_file_url).filter(Boolean).join('\\n'),\n imagenes_drive_urls: imagenes.map(m => m.drive_file_url).filter(Boolean).join('\\n'),\n videos_drive_urls: videos.map(m => m.drive_file_url).filter(Boolean).join('\\n'),\n\n total_media_drive: media.length,\n total_audio_drive: audios.length,\n total_imagenes_drive: imagenes.length,\n total_videos_drive: videos.length,\n\n ultima_actividad: new Date().toISOString(),\n\n etapa: 'PROCESANDO',\n estado: 'LISTO_PARA_ANALIZAR',\n motivo_revision: 'MEDIA_RECUPERADA',\n\n estado_procesamiento: 'MEDIA_RECUPERADA_Y_REGISTRADA'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -2704, 928 ], "id": "8ecc5a7e-5520-49e8-909c-1a59a47f28f9", "name": "Code - Consolidar media recuperada WhatsApp TEST" }, { "parameters": { "operation": "append", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 1962428033, "mode": "list", "cachedResultName": "wa_ejecuciones_media", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=1962428033" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "media_event_id": "={{ $json.media_event_id }}", "media_type": "={{ $json.media_type }}", "media_mime_type": "={{ $json.media_mime_type }}", "media_file_name": "={{ $json.drive_file_name }}", "drive_file_id": "={{ $json.drive_file_id }}", "drive_file_url": "={{ $json.drive_file_url }}", "fecha_guardado": "={{ new Date().toISOString() }}", "estado": "=MEDIA_GUARDADA_EN_DRIVE", "media_index": "={{ $json.media_index }}", "media_total": "={{ $json.media_total }}", "whatsapp_remote_jid": "={{ $json.whatsapp_remote_jid }}" }, "matchingColumns": [], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_event_id", "displayName": "media_event_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_type", "displayName": "media_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_mime_type", "displayName": "media_mime_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_file_name", "displayName": "media_file_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "drive_file_id", "displayName": "drive_file_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "drive_file_url", "displayName": "drive_file_url", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_guardado", "displayName": "fecha_guardado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_index", "displayName": "media_index", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_total", "displayName": "media_total", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "whatsapp_remote_jid", "displayName": "whatsapp_remote_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -2912, 928 ], "id": "900504c8-6e4d-4ce4-bb36-55739dae8408", "name": "Sheets - Guardar media Drive WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "motivo_revision": "={{ $json.motivo_revision }}", "estado": "={{ $json.estado }}", "etapa": "={{ $json.etapa }}", "ultima_actividad": "={{ $json.ultima_actividad }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -2496, 928 ], "id": "6f48b6b3-21bb-4f03-af72-68f67c2e0574", "name": "Sheets - Actualizar sesión media recuperada WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const data = $('Code - Consolidar media recuperada WhatsApp TEST').first().json || {};\n\nconst audioUrls = String(data.audio_drive_urls || '').trim();\nconst imagenesUrls = String(data.imagenes_drive_urls || '').trim();\nconst videosUrls = String(data.videos_drive_urls || '').trim();\n\nconst prompt = `\nAnaliza esta ejecución de propuesta usando la evidencia disponible.\n\nIMPORTANTE:\n- La nota de voz contiene la descripción principal de la ejecución.\n- Las imágenes muestran evidencia visual obligatoria.\n- Los videos son evidencia opcional adicional.\n- No inventes datos que no estén claros.\n- Si un dato no aparece, marca \"No identificado\".\n\nDebes devolver SOLO un JSON válido con esta estructura:\n\n{\n \"propuesta_referencia\": \"\",\n \"marca\": \"\",\n \"cliente\": \"\",\n \"pais\": \"\",\n \"ubicacion\": \"\",\n \"fecha_ejecucion\": \"\",\n \"que_se_implemento\": \"\",\n \"comentarios_resultados\": \"\",\n \"resumen_ejecucion\": \"\",\n \"nivel_confianza\": \"\",\n \"requiere_revision\": \"\",\n \"motivo_revision\": \"\"\n}\n\nDatos de control:\nsession_id: ${data.session_id}\nejecucion_id: ${data.ejecucion_id}\nmanager_nombre: ${data.manager_nombre}\nmanager_telefono: ${data.manager_telefono}\n\nArchivos de audio en Drive:\n${audioUrls || 'No hay audio registrado'}\n\nImágenes en Drive:\n${imagenesUrls || 'No hay imágenes registradas'}\n\nVideos en Drive:\n${videosUrls || 'No hay videos registrados'}\n`.trim();\n\nreturn [\n {\n json: {\n ...data,\n gemini_prompt: prompt,\n estado_procesamiento: 'PAQUETE_GEMINI_PREPARADO'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -2288, 928 ], "id": "6fefc255-e85b-4028-b9e9-b3f71abe1457", "name": "Code - Preparar paquete análisis Gemini WhatsApp TEST" }, { "parameters": { "rules": { "values": [ { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "leftValue": "={{ String($json.media_type || '').toLowerCase() }}", "rightValue": "audio", "operator": { "type": "string", "operation": "equals" }, "id": "ddf19bdc-2a61-4261-8950-02ea3026ba01" } ], "combinator": "and" }, "renameOutput": true, "outputKey": "audio" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "dcf1d733-7b80-4f67-b6f6-9e965a5bae24", "leftValue": "={{ String($json.media_type || '').toLowerCase() }}", "rightValue": "image", "operator": { "type": "string", "operation": "equals" } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "=image" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "bb7e48e1-8383-463c-a43e-7e8e5d1ae21d", "leftValue": "={{ String($json.media_type || '').toLowerCase() }}", "rightValue": "video", "operator": { "type": "string", "operation": "equals", "name": "filter.operator.equals" } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "VIDEO" } ] }, "options": {} }, "type": "n8n-nodes-base.switch", "typeVersion": 3.4, "position": [ -3248, 1376 ], "id": "235d1178-5ecf-4225-b1c5-792512a22877", "name": "Switch - Tipo media para Gemini WhatsApp TEST" }, { "parameters": { "jsCode": "const input = $input.first();\nconst data = input.json || {};\nconst binary = input.binary || {};\n\nif (!binary.data) {\n throw new Error('No llegó binary.data al nodo de audio para Gemini');\n}\n\nconst geminiPromptAudio = `\nAnaliza esta nota de voz enviada por WhatsApp para el flujo de registro de propuestas de Fulgencio Fumado.\n\nObjetivo:\nExtraer información útil del reporte y clasificar correctamente el tipo de reporte.\n\nPrimero, identifica el tipo de reporte usando exactamente uno de estos valores:\n\n1. PROPUESTA_EJECUTADA\nCuando el manager reporta evidencia de una propuesta que ya fue implementada, instalada, ejecutada, realizada o activada.\n\n2. PROPUESTA_EXTERNA\nCuando el manager reporta una propuesta, actividad, idea, ejecución, referencia o material recibido fuera del banco interno de propuestas y que debe documentarse.\n\n3. NO_DETERMINADO\nCuando la nota de voz no permite saber claramente si es una propuesta ejecutada o una propuesta externa.\n\nReglas para clasificar:\n- Si el manager dice explícitamente \"propuesta ejecutada\", clasifica como PROPUESTA_EJECUTADA.\n- Si el manager dice explícitamente \"ejecutada\", \"ya se ejecutó\", \"se implementó\", \"se realizó\", \"se instaló\" o expresiones equivalentes, clasifica como PROPUESTA_EJECUTADA.\n- Si el manager dice explícitamente \"propuesta externa\", clasifica como PROPUESTA_EXTERNA.\n- Si el manager dice \"externa\", \"fuera del banco\", \"recibida fuera del banco interno\", \"referencia externa\" o expresiones equivalentes, clasifica como PROPUESTA_EXTERNA.\n- Si solo hay una prueba de audio, saludo, ruido, conteo, información incompleta o no se entiende el objetivo, clasifica como NO_DETERMINADO.\n- No inventes el tipo de reporte.\n- Si hay duda, usa NO_DETERMINADO.\n\nAdemás, extrae la mayor cantidad posible de información útil:\n- Nombre o referencia de la propuesta\n- Marca o cliente\n- País\n- Ubicación\n- Fecha de ejecución o fecha del reporte\n- Qué se implementó o qué se está reportando\n- Comentarios o resultados observados\n- Si el manager menciona que es ejecutada, externa o no queda claro\n\nDevuelve el análisis en texto claro y estructurado, incluyendo obligatoriamente estas líneas:\n\nTipo de reporte detectado: PROPUESTA_EJECUTADA | PROPUESTA_EXTERNA | NO_DETERMINADO\nConfianza del tipo de reporte: ALTA | MEDIA | BAJA\nMotivo del tipo de reporte: explicación breve\n\nNo inventes datos. Si algo no está claro, indica \"No identificado\".\n`.trim();\n\nreturn [\n {\n json: {\n ...data,\n gemini_prompt_audio: geminiPromptAudio\n },\n binary\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -2768, 1200 ], "id": "e3cfe03a-110b-4470-9011-e4705dcde4a7", "name": "Code - Preparar Gemini audio WhatsApp TEST" }, { "parameters": { "resource": "audio", "operation": "analyze", "modelId": { "__rl": true, "value": "models/gemini-3.1-flash-lite", "mode": "list", "cachedResultName": "models/gemini-3.1-flash-lite" }, "text": "={{ $json.gemini_prompt_audio }}", "inputType": "binary", "options": {} }, "type": "@n8n/n8n-nodes-langchain.googleGemini", "typeVersion": 1.2, "position": [ -2560, 1200 ], "id": "18ddf06b-efd7-49b0-b4bf-0f68e4c91a20", "name": "Gemini - Analizar audio WhatsApp TEST", "credentials": { "googlePalmApi": { "id": "6Jdcd9FONxG35YuJ", "name": "Isaac - Gemini Api Gratis" } } }, { "parameters": { "jsCode": "const resultados = $input.all();\nconst fuentes = $('Code - Preparar Gemini audio WhatsApp TEST').all();\n\nconst extraerTexto = (obj) => {\n if (!obj || typeof obj !== 'object') return '';\n\n if (typeof obj.text === 'string') return obj.text;\n if (typeof obj.output === 'string') return obj.output;\n if (typeof obj.response === 'string') return obj.response;\n if (typeof obj.content === 'string') return obj.content;\n\n if (Array.isArray(obj.candidates)) {\n return JSON.stringify(obj.candidates);\n }\n\n return JSON.stringify(obj);\n};\n\nreturn resultados.map((item, index) => {\n const gemini = item.json || {};\n const media = fuentes[index]?.json || fuentes[0]?.json || {};\n\n return {\n json: {\n session_id: media.session_id || '',\n ejecucion_id: media.ejecucion_id || '',\n media_event_id: media.media_event_id || '',\n media_type: 'audio',\n media_file_name: media.media_file_name || '',\n analisis_texto: extraerTexto(gemini),\n fecha_analisis: new Date().toISOString(),\n estado: 'ANALISIS_AUDIO_COMPLETADO',\n media_index: media.media_index || '',\n media_total: media.media_total || ''\n }\n };\n});" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -2352, 1200 ], "id": "2587e982-2403-450c-9419-a9de41d3c5a1", "name": "Code - Normalizar análisis audio WhatsApp TEST" }, { "parameters": { "jsCode": "const input = $input.first();\nconst data = input.json || {};\nconst binary = input.binary || {};\n\nif (!binary.data) {\n throw new Error('No llegó binary.data al nodo de imagen para Gemini');\n}\n\nreturn [\n {\n json: {\n ...data,\n gemini_prompt_imagen: `Analiza esta imagen como evidencia de una propuesta ejecutada.\n\nDescribe:\n- Qué se observa en la imagen\n- Elementos de marca visibles\n- Materiales instalados o implementados\n- Posible ubicación visible\n- Calidad de la implementación\n- Detalles relevantes para documentar la ejecución\n\nNo inventes datos que no sean visibles.`\n },\n binary\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -2752, 1424 ], "id": "68e8b0de-373c-48fa-a762-8c0e45d64c15", "name": "Code - Preparar Gemini imagen WhatsApp TEST" }, { "parameters": { "resource": "image", "operation": "analyze", "modelId": { "__rl": true, "value": "models/gemini-3.1-flash-lite", "mode": "list", "cachedResultName": "models/gemini-3.1-flash-lite" }, "text": "={{ $json.gemini_prompt_imagen }}", "inputType": "binary", "options": {} }, "type": "@n8n/n8n-nodes-langchain.googleGemini", "typeVersion": 1.2, "position": [ -2544, 1424 ], "id": "1baf8c8e-95cf-42fe-af4f-fcbc71a557fc", "name": "Gemini - Analizar imagen WhatsApp TEST", "credentials": { "googlePalmApi": { "id": "6Jdcd9FONxG35YuJ", "name": "Isaac - Gemini Api Gratis" } } }, { "parameters": { "jsCode": "const resultados = $input.all();\nconst fuentes = $('Code - Preparar Gemini imagen WhatsApp TEST').all();\n\nconst extraerTexto = (obj) => {\n if (!obj || typeof obj !== 'object') return '';\n\n if (typeof obj.text === 'string') return obj.text;\n if (typeof obj.output === 'string') return obj.output;\n if (typeof obj.response === 'string') return obj.response;\n if (typeof obj.content === 'string') return obj.content;\n\n if (Array.isArray(obj.candidates)) {\n return JSON.stringify(obj.candidates);\n }\n\n return JSON.stringify(obj);\n};\n\nreturn resultados.map((item, index) => {\n const gemini = item.json || {};\n const media = fuentes[index]?.json || fuentes[0]?.json || {};\n\n return {\n json: {\n session_id: media.session_id || '',\n ejecucion_id: media.ejecucion_id || '',\n media_event_id: media.media_event_id || '',\n media_type: 'image',\n media_file_name: media.media_file_name || '',\n analisis_texto: extraerTexto(gemini),\n fecha_analisis: new Date().toISOString(),\n estado: 'ANALISIS_IMAGEN_COMPLETADO',\n media_index: media.media_index || '',\n media_total: media.media_total || ''\n }\n };\n});" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -2336, 1424 ], "id": "e1bc474b-82b0-4a4c-9ef7-e5f52b2b7503", "name": "Code - Normalizar análisis imagen WhatsApp TEST" }, { "parameters": { "jsCode": "const input = $input.first();\nconst data = input.json || {};\nconst binary = input.binary || {};\n\nif (!binary.data) {\n throw new Error('No llegó binary.data al nodo de video para Gemini');\n}\n\nreturn [\n {\n json: {\n ...data,\n gemini_prompt_video: `Analiza este video como evidencia de una propuesta ejecutada.\n\nDescribe:\n- Qué se observa en el video\n- Elementos de marca visibles\n- Materiales instalados o implementados\n- Interacciones o movimientos relevantes\n- Posible ubicación visible\n- Calidad de la implementación\n- Resultados observables\n\nNo inventes datos que no sean visibles.`\n },\n binary\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -2880, 1664 ], "id": "5bfc997b-a538-4d96-8d7d-e4abe8e4cf9f", "name": "Code - Preparar Gemini video WhatsApp TEST" }, { "parameters": { "resource": "video", "operation": "analyze", "modelId": { "__rl": true, "value": "models/gemini-3.1-flash-lite", "mode": "list", "cachedResultName": "models/gemini-3.1-flash-lite" }, "text": "={{ $json.gemini_prompt_video }}", "inputType": "binary", "options": {} }, "type": "@n8n/n8n-nodes-langchain.googleGemini", "typeVersion": 1.2, "position": [ -2672, 1664 ], "id": "5767efd8-bfb6-4221-964d-743b417be1aa", "name": "Analyze video", "credentials": { "googlePalmApi": { "id": "6Jdcd9FONxG35YuJ", "name": "Isaac - Gemini Api Gratis" } } }, { "parameters": { "jsCode": "const resultados = $input.all();\nconst fuentes = $('Code - Preparar Gemini video WhatsApp TEST').all();\n\nconst extraerTexto = (obj) => {\n if (!obj || typeof obj !== 'object') return '';\n\n if (typeof obj.text === 'string') return obj.text;\n if (typeof obj.output === 'string') return obj.output;\n if (typeof obj.response === 'string') return obj.response;\n if (typeof obj.content === 'string') return obj.content;\n\n if (Array.isArray(obj.candidates)) {\n return JSON.stringify(obj.candidates);\n }\n\n return JSON.stringify(obj);\n};\n\nreturn resultados.map((item, index) => {\n const gemini = item.json || {};\n const media = fuentes[index]?.json || fuentes[0]?.json || {};\n\n return {\n json: {\n session_id: media.session_id || '',\n ejecucion_id: media.ejecucion_id || '',\n media_event_id: media.media_event_id || '',\n media_type: 'video',\n media_file_name: media.media_file_name || '',\n analisis_texto: extraerTexto(gemini),\n fecha_analisis: new Date().toISOString(),\n estado: 'ANALISIS_VIDEO_COMPLETADO',\n media_index: media.media_index || '',\n media_total: media.media_total || ''\n }\n };\n});" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -2464, 1664 ], "id": "c035db53-87b9-4fc2-9346-d5228bbac7b7", "name": "Code - Normalizar análisis video WhatsApp TEST" }, { "parameters": { "operation": "append", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 1107394537, "mode": "list", "cachedResultName": "wa_ejecuciones_analisis_media", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=1107394537" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "media_event_id": "={{ $json.media_event_id }}", "media_type": "={{ $json.media_type }}", "media_file_name": "={{ $json.media_file_name }}", "analisis_texto": "={{ $json.analisis_texto }}", "fecha_analisis": "={{ $json.fecha_analisis }}", "estado": "={{ $json.estado }}", "media_index": "={{ $json.media_index }}", "media_total": "={{ $json.media_total }}" }, "matchingColumns": [], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_event_id", "displayName": "media_event_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_type", "displayName": "media_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_file_name", "displayName": "media_file_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "analisis_texto", "displayName": "analisis_texto", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_analisis", "displayName": "fecha_analisis", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_index", "displayName": "media_index", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_total", "displayName": "media_total", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -1872, 1376 ], "id": "55f75826-cb50-4857-b58f-11bce7703b47", "name": "Sheets - Guardar análisis media Gemini WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 1107394537, "mode": "list", "cachedResultName": "wa_ejecuciones_analisis_media", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=1107394537" }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -1184, 1360 ], "id": "d68f5e66-a1cb-48cc-afbd-07677f3145cb", "name": "Sheets - Leer análisis media Gemini WhatsApp TEST", "alwaysOutputData": true, "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const cierre = $('Code - Consolidar media recuperada WhatsApp TEST').first().json || {};\nconst evento = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\n\nconst sessionId = String(cierre.session_id || evento.session_id || '').trim();\nconst ejecucionId = String(cierre.ejecucion_id || evento.ejecucion_id || sessionId).trim();\n\nconst rows = $input.all().map(item => item.json || {});\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst extraerTextoAnalisis = (valor) => {\n if (!valor) return '';\n\n if (typeof valor === 'object') {\n return extraerDesdeObjeto(valor);\n }\n\n const texto = String(valor || '').trim();\n\n if (!texto) return '';\n\n try {\n const obj = JSON.parse(texto);\n const extraido = extraerDesdeObjeto(obj);\n if (extraido) return extraido;\n } catch (error) {}\n\n return texto;\n};\n\nconst extraerDesdeObjeto = (obj) => {\n if (!obj || typeof obj !== 'object') return '';\n\n if (typeof obj.text === 'string') return obj.text.trim();\n if (typeof obj.output === 'string') return obj.output.trim();\n if (typeof obj.response === 'string') return obj.response.trim();\n if (typeof obj.content === 'string') return obj.content.trim();\n\n if (Array.isArray(obj.content?.parts)) {\n const txt = obj.content.parts.map(p => p.text || '').join('\\n').trim();\n if (txt) return txt;\n }\n\n if (Array.isArray(obj.parts)) {\n const txt = obj.parts.map(p => p.text || '').join('\\n').trim();\n if (txt) return txt;\n }\n\n if (Array.isArray(obj.candidates)) {\n const txt = obj.candidates\n .flatMap(c => c.content?.parts || [])\n .map(p => p.text || '')\n .join('\\n')\n .trim();\n\n if (txt) return txt;\n }\n\n if (obj.message && typeof obj.message === 'object') {\n const txt = extraerDesdeObjeto(obj.message);\n if (txt) return txt;\n }\n\n if (obj.data && typeof obj.data === 'object') {\n const txt = extraerDesdeObjeto(obj.data);\n if (txt) return txt;\n }\n\n return '';\n};\n\nconst analisisSesion = rows.filter(row => {\n return String(row.session_id || '').trim() === sessionId;\n});\n\n// Evitar duplicados por pruebas manuales\nconst vistos = new Set();\nconst analisisUnicos = [];\n\nfor (const row of analisisSesion) {\n const key = `${row.media_event_id || ''}_${row.media_type || ''}`;\n\n if (vistos.has(key)) continue;\n\n vistos.add(key);\n\n analisisUnicos.push({\n ...row,\n analisis_texto_limpio: extraerTextoAnalisis(row.analisis_texto)\n });\n}\n\nconst audios = analisisUnicos.filter(r => String(r.media_type || '').toLowerCase() === 'audio');\nconst imagenes = analisisUnicos.filter(r => String(r.media_type || '').toLowerCase() === 'image');\nconst videos = analisisUnicos.filter(r => String(r.media_type || '').toLowerCase() === 'video');\n\nconst textoAudio = audios.map(r => r.analisis_texto_limpio).filter(Boolean).join('\\n\\n---\\n\\n');\nconst textoImagenes = imagenes.map(r => r.analisis_texto_limpio).filter(Boolean).join('\\n\\n---\\n\\n');\nconst textoVideos = videos.map(r => r.analisis_texto_limpio).filter(Boolean).join('\\n\\n---\\n\\n');\n\nconst textoTotal = [textoAudio, textoImagenes, textoVideos].filter(Boolean).join('\\n\\n');\n\nif (!textoTotal) {\n throw new Error(`No hay texto limpio de análisis para la sesión ${sessionId}. Revisa wa_ejecuciones_analisis_media.`);\n}\n\nconst managerNombre =\n limpiar(cierre.manager_nombre) ||\n limpiar(evento.manager_nombre) ||\n 'No identificado';\n\nconst managerTelefono =\n limpiar(cierre.manager_telefono) ||\n limpiar(evento.manager_telefono) ||\n 'No identificado';\n\nconst promptFinal = `\nEres un analista de propuestas de GomezLee Marketing.\n\nCon base en el análisis del audio, imágenes y videos, genera un JSON final estructurado para registrar el reporte.\n\nReglas generales:\n- Devuelve SOLO JSON válido.\n- No uses markdown.\n- No inventes información.\n- Si un dato no está claro, usa \"No identificado\".\n- El audio tiene prioridad para datos declarados por el manager.\n- Las imágenes y videos sirven para validar evidencia visual.\n- Si hay dudas importantes, requiere_revision debe ser \"SI\"; si todo está claro, \"NO\".\n- nivel_confianza debe ser \"ALTA\", \"MEDIA\" o \"BAJA\".\n- resumen_ejecucion debe ser una síntesis útil para un reporte ejecutivo.\n- que_se_implemento debe describir claramente lo ejecutado o reportado.\n\nClasifica también el tipo de reporte.\n\ntipo_reporte debe ser exactamente uno de estos valores:\n- PROPUESTA_EJECUTADA\n- PROPUESTA_EXTERNA\n- NO_DETERMINADO\n\ntipo_reporte_confianza debe ser exactamente uno de estos valores:\n- ALTA\n- MEDIA\n- BAJA\n\ntipo_reporte_motivo debe explicar brevemente por qué se eligió ese tipo.\n\nReglas para tipo_reporte:\n- Si el audio menciona claramente que es una propuesta ejecutada, usa PROPUESTA_EJECUTADA.\n- Si el audio menciona que ya se ejecutó, se implementó, se instaló, se realizó o se activó una propuesta, usa PROPUESTA_EJECUTADA.\n- Si el audio menciona claramente que es una propuesta externa, usa PROPUESTA_EXTERNA.\n- Si el audio menciona algo recibido fuera del banco interno, referencia externa, idea externa, actividad externa o material externo que debe documentarse, usa PROPUESTA_EXTERNA.\n- Si el audio es una prueba, saludo, ruido, información incompleta o no permite clasificar, usa NO_DETERMINADO.\n- No uses las imágenes para inventar el tipo si el audio no lo confirma.\n- El audio tiene prioridad para determinar el tipo de reporte.\n- Si hay duda entre ejecutada y externa, usa NO_DETERMINADO.\n\nEstructura obligatoria:\n\n{\n \"tipo_reporte\": \"\",\n \"tipo_reporte_confianza\": \"\",\n \"tipo_reporte_motivo\": \"\",\n \"propuesta_referencia\": \"\",\n \"marca\": \"\",\n \"cliente\": \"\",\n \"pais\": \"\",\n \"ubicacion\": \"\",\n \"fecha_ejecucion\": \"\",\n \"que_se_implemento\": \"\",\n \"comentarios_resultados\": \"\",\n \"resumen_ejecucion\": \"\",\n \"evidencia_audio_resumen\": \"\",\n \"evidencia_imagenes_resumen\": \"\",\n \"evidencia_videos_resumen\": \"\",\n \"nivel_confianza\": \"\",\n \"requiere_revision\": \"\",\n \"motivo_revision\": \"\"\n}\n\nDatos de control:\nsession_id: ${sessionId}\nejecucion_id: ${ejecucionId}\nmanager_nombre: ${managerNombre}\nmanager_telefono: ${managerTelefono}\n\nANÁLISIS DEL AUDIO:\n${textoAudio || 'No hay análisis de audio disponible.'}\n\nANÁLISIS DE IMÁGENES:\n${textoImagenes || 'No hay análisis de imágenes disponible.'}\n\nANÁLISIS DE VIDEOS:\n${textoVideos || 'No hay análisis de videos disponible.'}\n`.trim();\n\nreturn [\n {\n json: {\n ...cierre,\n\n session_id: sessionId,\n ejecucion_id: ejecucionId,\n\n manager_nombre: managerNombre,\n manager_telefono: managerTelefono,\n canal_origen: cierre.canal_origen || evento.canal_origen || 'WHATSAPP',\n\n total_analisis_media: analisisUnicos.length,\n total_analisis_audio: audios.length,\n total_analisis_imagenes: imagenes.length,\n total_analisis_videos: videos.length,\n\n texto_audio: textoAudio,\n texto_imagenes: textoImagenes,\n texto_videos: textoVideos,\n\n gemini_prompt_final: promptFinal,\n\n estado_procesamiento: 'ANALISIS_MEDIA_CONSOLIDADO'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -976, 1360 ], "id": "2a9d4918-8e52-4633-8369-c4c8397e9129", "name": "Code - Consolidar análisis media Gemini WhatsApp TEST" }, { "parameters": { "modelId": { "__rl": true, "value": "models/gemini-3.1-flash-lite", "mode": "list", "cachedResultName": "models/gemini-3.1-flash-lite" }, "messages": { "values": [ { "content": "={{ $json.gemini_prompt_final }}" } ] }, "builtInTools": {}, "options": {} }, "type": "@n8n/n8n-nodes-langchain.googleGemini", "typeVersion": 1.2, "position": [ -768, 1360 ], "id": "76e59ce7-1844-40e3-bd7d-1e4101838aad", "name": "Gemini - Generar JSON final propuesta ejecutada TEST", "credentials": { "googlePalmApi": { "id": "6Jdcd9FONxG35YuJ", "name": "Isaac - Gemini Api Gratis" } } }, { "parameters": { "jsCode": "const gemini = $json || {};\nconst base = $('Code - Consolidar análisis media Gemini WhatsApp TEST').first().json || {};\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst extraerTexto = (obj) => {\n if (!obj || typeof obj !== 'object') return '';\n\n if (typeof obj.text === 'string') return obj.text;\n if (typeof obj.output === 'string') return obj.output;\n if (typeof obj.response === 'string') return obj.response;\n if (typeof obj.content === 'string') return obj.content;\n\n if (Array.isArray(obj.content?.parts)) {\n const txt = obj.content.parts.map(p => p.text || '').join('\\n').trim();\n if (txt) return txt;\n }\n\n if (Array.isArray(obj.parts)) {\n const txt = obj.parts.map(p => p.text || '').join('\\n').trim();\n if (txt) return txt;\n }\n\n if (Array.isArray(obj.candidates)) {\n const txt = obj.candidates\n .flatMap(c => c.content?.parts || [])\n .map(p => p.text || '')\n .join('\\n')\n .trim();\n\n if (txt) return txt;\n }\n\n if (obj.message && typeof obj.message === 'object') {\n const txt = extraerTexto(obj.message);\n if (txt) return txt;\n }\n\n if (obj.data && typeof obj.data === 'object') {\n const txt = extraerTexto(obj.data);\n if (txt) return txt;\n }\n\n return JSON.stringify(obj);\n};\n\nlet texto = extraerTexto(gemini).trim();\n\ntexto = texto\n .replace(/^```json/i, '')\n .replace(/^```/i, '')\n .replace(/```$/i, '')\n .trim();\n\nlet parsed = {};\n\ntry {\n parsed = JSON.parse(texto);\n} catch (error) {\n parsed = {\n tipo_reporte: 'NO_DETERMINADO',\n tipo_reporte_confianza: 'BAJA',\n tipo_reporte_motivo: `No se pudo parsear el JSON final de Gemini: ${error.message}`,\n\n propuesta_referencia: 'No identificado',\n marca: 'No identificado',\n cliente: 'No identificado',\n pais: 'No identificado',\n ubicacion: 'No identificado',\n fecha_ejecucion: 'No identificado',\n que_se_implemento: 'No identificado',\n comentarios_resultados: '',\n resumen_ejecucion: texto || 'No se pudo parsear el JSON de Gemini.',\n evidencia_audio_resumen: base.texto_audio || '',\n evidencia_imagenes_resumen: base.texto_imagenes || '',\n evidencia_videos_resumen: base.texto_videos || '',\n nivel_confianza: 'BAJA',\n requiere_revision: 'SI',\n motivo_revision: `Error parseando JSON final: ${error.message}`\n };\n}\n\nconst valor = (campo, fallback = 'No identificado') => {\n const v = limpiar(campo);\n if (!v) return fallback;\n if (v.toLowerCase() === 'undefined') return fallback;\n if (v.toLowerCase() === 'null') return fallback;\n return v;\n};\n\nconst normalizarTipoReporte = (valorTipo) => {\n const texto = String(valorTipo ?? '')\n .trim()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toUpperCase()\n .replace(/\\s+/g, '_');\n\n if (\n texto === 'PROPUESTA_EJECUTADA' ||\n texto === 'EJECUTADA' ||\n texto === 'EJECUTADO'\n ) {\n return 'PROPUESTA_EJECUTADA';\n }\n\n if (\n texto === 'PROPUESTA_EXTERNA' ||\n texto === 'EXTERNA' ||\n texto === 'EXTERNO'\n ) {\n return 'PROPUESTA_EXTERNA';\n }\n\n return 'NO_DETERMINADO';\n};\n\nconst normalizarConfianza = (valorConfianza) => {\n const texto = String(valorConfianza ?? '')\n .trim()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toUpperCase();\n\n if (['ALTA', 'MEDIA', 'BAJA'].includes(texto)) {\n return texto;\n }\n\n return 'BAJA';\n};\n\nconst ahora = new Date().toISOString();\n\nconst requiereRevision = valor(parsed.requiere_revision, 'SI').toUpperCase();\n\nconst estadoRevision = requiereRevision === 'SI'\n ? 'PENDIENTE_REVISION'\n : 'ANALISIS_COMPLETADO';\n\nconst evidenciaImagenes = valor(parsed.evidencia_imagenes_resumen, '');\nconst evidenciaVideos = valor(parsed.evidencia_videos_resumen, '');\n\nconst elementosDetectados = [\n evidenciaImagenes,\n evidenciaVideos\n].filter(Boolean).join('\\n\\n');\n\nconst tags = [\n parsed.marca,\n parsed.cliente,\n parsed.pais,\n parsed.ubicacion\n]\n .map(v => valor(v, ''))\n .filter(Boolean)\n .join(', ');\n\nconst tipoReporte = normalizarTipoReporte(parsed.tipo_reporte);\nconst tipoReporteConfianza = normalizarConfianza(parsed.tipo_reporte_confianza);\nconst tipoReporteMotivo = valor(parsed.tipo_reporte_motivo, 'No identificado');\n\nreturn [\n {\n json: {\n ...base,\n\n ejecucion_id: base.ejecucion_id || base.session_id || '',\n session_id: base.session_id || '',\n\n fecha_recepcion: base.fecha_recepcion || ahora,\n fecha_ejecucion: valor(parsed.fecha_ejecucion),\n\n manager_nombre: valor(base.manager_nombre),\n manager_telefono: valor(base.manager_telefono),\n canal_origen: base.canal_origen || 'WHATSAPP',\n\n tipo_reporte: tipoReporte,\n tipo_reporte_confianza: tipoReporteConfianza,\n tipo_reporte_motivo: tipoReporteMotivo,\n\n propuesta_referencia: valor(parsed.propuesta_referencia),\n\n propuesta_match_estado: 'PENDIENTE',\n propuesta_match_confianza: '',\n propuesta_nombre_banco: '',\n propuesta_link_banco: '',\n propuesta_match_revision: 'PENDIENTE_MATCH_BANCO',\n\n marca: valor(parsed.marca),\n cliente: valor(parsed.cliente),\n pais: valor(parsed.pais),\n ubicacion: valor(parsed.ubicacion),\n\n comentario_original: valor(parsed.evidencia_audio_resumen || base.texto_audio, ''),\n resumen_ia: valor(parsed.resumen_ejecucion, ''),\n descripcion_ejecucion: valor(parsed.que_se_implemento),\n elementos_detectados: elementosDetectados,\n tags,\n\n media_folder_url: base.media_folder_url || '',\n presentacion_ejecucion_url: '',\n\n fotos_count: base.total_analisis_imagenes || 0,\n videos_count: base.total_analisis_videos || 0,\n audios_count: base.total_analisis_audio || 0,\n\n estado_revision: estadoRevision,\n motivo_revision: valor(parsed.motivo_revision, ''),\n ultima_actualizacion: ahora,\n\n transcripcion_audio: valor(parsed.evidencia_audio_resumen || base.texto_audio, ''),\n\n nivel_confianza: valor(parsed.nivel_confianza, 'BAJA'),\n requiere_revision: requiereRevision,\n\n gemini_json_raw: texto\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -416, 1360 ], "id": "891ffabf-cb1a-415a-8b29-3facb39f4efa", "name": "Code - Normalizar JSON final propuesta ejecutada TEST" }, { "parameters": { "operation": "append", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 737153956, "mode": "list", "cachedResultName": "propuestas_ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=737153956" }, "columns": { "mappingMode": "defineBelow", "value": { "ejecucion_id": "={{ $json.ejecucion_id }}", "fecha_recepcion": "={{ $json.fecha_recepcion }}", "fecha_ejecucion": "={{ $json.fecha_ejecucion }}", "manager_nombre": "={{ $json.manager_nombre }}", "manager_telefono": "={{ $json.manager_telefono }}", "propuesta_referencia": "={{ $json.propuesta_referencia }}", "propuesta_match_estado": "={{ $json.propuesta_match_estado }}", "propuesta_match_confianza": "={{ $json.propuesta_match_confianza }}", "propuesta_nombre_banco": "={{ $json.propuesta_nombre_banco }}", "propuesta_link_banco": "={{ $json.propuesta_link_banco }}", "marca": "={{ $json.marca }}", "cliente": "={{ $json.cliente }}", "pais": "={{ $json.pais }}", "ubicacion": "={{ $json.ubicacion }}", "comentario_original": "={{ $json.comentario_original }}", "resumen_ia": "={{ $json.resumen_ia }}", "descripcion_ejecucion": "={{ $json.descripcion_ejecucion }}", "elementos_detectados": "={{ $json.elementos_detectados }}", "tags": "={{ $json.tags }}", "media_folder_url": "={{ $json.media_folder_url }}", "presentacion_ejecucion_url": "={{ $json.presentacion_ejecucion_url }}", "fotos_count": "={{ $json.fotos_count }}", "videos_count": "={{ $json.videos_count }}", "audios_count": "={{ $json.audios_count }}", "estado_revision": "={{ $json.estado_revision }}", "motivo_revision": "={{ $json.motivo_revision }}", "ultima_actualizacion": "={{ $json.ultima_actualizacion }}", "session_id": "={{ $json.session_id }}", "canal_origen": "={{ $json.canal_origen }}", "transcripcion_audio": "={{ $json.transcripcion_audio }}", "propuesta_match_revision": "={{ $json.propuesta_match_revision }}", "tipo_reporte": "={{ $json.tipo_reporte }}", "tipo_reporte_confianza": "={{ $json.tipo_reporte_confianza }}", "tipo_reporte_motivo": "={{ $json.tipo_reporte_motivo }}" }, "matchingColumns": [], "schema": [ { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_recepcion", "displayName": "fecha_recepcion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_ejecucion", "displayName": "fecha_ejecucion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_referencia", "displayName": "propuesta_referencia", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_match_estado", "displayName": "propuesta_match_estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_match_confianza", "displayName": "propuesta_match_confianza", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_nombre_banco", "displayName": "propuesta_nombre_banco", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_link_banco", "displayName": "propuesta_link_banco", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "marca", "displayName": "marca", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "cliente", "displayName": "cliente", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "pais", "displayName": "pais", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ubicacion", "displayName": "ubicacion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "comentario_original", "displayName": "comentario_original", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "resumen_ia", "displayName": "resumen_ia", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "descripcion_ejecucion", "displayName": "descripcion_ejecucion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "elementos_detectados", "displayName": "elementos_detectados", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "tags", "displayName": "tags", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_folder_url", "displayName": "media_folder_url", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "presentacion_ejecucion_url", "displayName": "presentacion_ejecucion_url", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fotos_count", "displayName": "fotos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audios_count", "displayName": "audios_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado_revision", "displayName": "estado_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actualizacion", "displayName": "ultima_actualizacion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "transcripcion_audio", "displayName": "transcripcion_audio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_match_revision", "displayName": "propuesta_match_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "tipo_reporte", "displayName": "tipo_reporte", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "tipo_reporte_confianza", "displayName": "tipo_reporte_confianza", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "tipo_reporte_motivo", "displayName": "tipo_reporte_motivo", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ 2144, 1536 ], "id": "705689dd-2988-4aa4-835b-3386b5d24e13", "name": "Sheets - Guardar propuesta ejecutada final TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ultima_actividad": "={{ new Date().toISOString() }}", "etapa": "={{ 'COMPLETADO' }}", "estado": "={{ 'ANALISIS_COMPLETADO' }}", "motivo_revision": "={{ $json.motivo_revision || 'ANALISIS_COMPLETADO' }}", "tipo_reporte": "={{ $json.tipo_reporte }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "is_group", "displayName": "is_group", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_jid", "displayName": "group_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_name", "displayName": "group_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_jid", "displayName": "sender_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_phone", "displayName": "sender_phone", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_name", "displayName": "sender_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "country_code", "displayName": "country_code", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "pais_detectado", "displayName": "pais_detectado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "idioma_flujo", "displayName": "idioma_flujo", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "tipo_reporte", "displayName": "tipo_reporte", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ 8800, 1440 ], "id": "49036fad-dbd7-44d8-8597-b098eadf0ed3", "name": "Sheets - Actualizar sesión análisis completado WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst normalizado = getNodeJson('Code - Normalizar JSON final propuesta ejecutada TEST');\nconst presentacion = getNodeJson('Code - Normalizar link presentación ejecución TEST');\nconst basePaso = getNodeJson('Code - Resolver paso activo WhatsApp TEST');\nconst actual = $json || {};\n\nlet data = {\n ...basePaso,\n ...normalizado,\n ...actual,\n ...presentacion,\n};\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst destinoBase = limpiar(\n data.whatsapp_to ||\n data.group_jid ||\n data.whatsapp_remote_jid ||\n data.sesion_activa?.group_jid ||\n data.sesion_activa?.whatsapp_to ||\n data.sesion_activa?.whatsapp_remote_jid ||\n data.manager_telefono ||\n data.sender_phone ||\n ''\n);\n\nif (!destinoBase) {\n throw new Error('No se encontró destino WhatsApp para enviar mensaje final.');\n}\n\nlet whatsappTo = destinoBase;\n\n// IMPORTANTE:\n// Si es grupo, conservar @g.us.\n// No convertirlo a @s.whatsapp.net.\nif (whatsappTo.includes('@g.us')) {\n whatsappTo = whatsappTo;\n} else if (whatsappTo.includes('@s.whatsapp.net')) {\n whatsappTo = whatsappTo;\n} else {\n whatsappTo = `${whatsappTo.replace(/\\D/g, '')}@s.whatsapp.net`;\n}\n\nconst presentacionUrl = limpiar(\n data.presentacion_ejecucion_url ||\n data.presentation_url ||\n ''\n);\n\nconst bloquePresentacion = presentacionUrl\n ? `\\n\\nPresentación:\\n${presentacionUrl}`\n : '';\n\nconst idioma = limpiar(\n data.idioma_flujo ||\n data.sesion_activa?.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nlet whatsappText = '';\n\nif (idioma === 'EN') {\n whatsappText = `✅ Report received and processed successfully.\n\nYour evidence was registered in the proposal evidence bank.${bloquePresentacion}\n\nThank you for completing the report.`;\n} else {\n whatsappText = `✅ Reporte recibido y procesado correctamente.\n\nTu evidencia fue registrada en el banco de propuestas.${bloquePresentacion}\n\nGracias por completar el reporte.`;\n}\n\nreturn [\n {\n json: {\n ...data,\n\n whatsapp_to: whatsappTo,\n whatsapp_text: whatsappText,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 9088, 1408 ], "id": "914441cf-483f-4aab-a30e-7c619a34e49e", "name": "Code - Preparar mensaje final WhatsApp TEST" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "7591a797-d76a-48e7-8e75-9a41cff6e974", "leftValue": "={{ Number($json.media_index || 0) === Number($json.media_total || 0) }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ -1600, 1360 ], "id": "9f291944-bbf8-4584-b997-9e842caca9cb", "name": "IF - Es último análisis media WhatsApp TEST" }, { "parameters": { "amount": 15 }, "type": "n8n-nodes-base.wait", "typeVersion": 1.1, "position": [ -1376, 1360 ], "id": "fef0aeef-418b-419d-a6ca-6a9440494139", "name": "Wait", "webhookId": "c6d9670c-1a1f-43c9-89b3-9952b1501850" }, { "parameters": { "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 737153956, "mode": "list", "cachedResultName": "propuestas_ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=737153956" }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ 384, 1376 ], "id": "7c547eeb-0528-460f-a9a2-d3533e43bb55", "name": "Sheets - Leer propuestas ejecutadas final TEST", "alwaysOutputData": true, "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const base = $('Code - Normalizar JSON final propuesta ejecutada TEST').first().json || {};\nconst rows = $input.all().map(item => item.json || {});\n\nconst sessionId = String(base.session_id || '').trim();\nconst ejecucionId = String(base.ejecucion_id || '').trim();\n\nconst existente = rows.find(row => {\n const rowSessionId = String(row.session_id || '').trim();\n const rowEjecucionId = String(row.ejecucion_id || '').trim();\n\n return (\n (sessionId && rowSessionId === sessionId) ||\n (ejecucionId && rowEjecucionId === ejecucionId)\n );\n});\n\nreturn [\n {\n json: {\n ...base,\n\n propuesta_final_ya_existe: Boolean(existente),\n propuesta_final_row_number: existente?.row_number || '',\n propuesta_final_existente: existente || null,\n\n estado_anti_duplicado: existente\n ? 'PROPUESTA_FINAL_YA_EXISTE'\n : 'PROPUESTA_FINAL_NUEVA'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 592, 1376 ], "id": "dc39153f-ddcc-4f21-abc8-7a4b42074807", "name": "Code - Verificar duplicado propuesta ejecutada TEST" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "80785332-9e50-44d5-9fd5-ecf9610876fd", "leftValue": "={{ $json.propuesta_final_ya_existe === true }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ 800, 1376 ], "id": "210119ea-e335-4d9d-abd2-b2aa5b0af24a", "name": "IF - Propuesta final ya existe TEST" }, { "parameters": { "jsCode": "const data = $('Code - Normalizar JSON final propuesta ejecutada TEST').first().json || {};\n\nconst templateId = '10PZtoMFuiXczGM9Itbcjce4EKBUhiztFbXZiTWX2DKc';\nconst folderId = '1q92b3lMw_fcjD1YUtp5YcJKne49zOfpc';\n\nconst limpiar = (valor) =>\n String(valor || '')\n .replace(/[\\\\/:*?\"<>|]/g, '-')\n .replace(/\\s+/g, ' ')\n .trim();\n\nconst marca = limpiar(data.marca || 'Marca no identificada');\nconst referencia = limpiar(data.propuesta_referencia || 'Propuesta ejecutada');\nconst fecha = limpiar(data.fecha_ejecucion || new Date().toISOString().slice(0, 10));\nconst sessionId = limpiar(data.session_id || data.ejecucion_id || Date.now());\n\nconst presentationName = `${fecha} - ${marca} - ${referencia} - ${sessionId}`;\n\nreturn [\n {\n json: {\n ...data,\n\n slides_template_id: templateId,\n slides_folder_id: folderId,\n presentation_name: presentationName,\n\n estado_presentacion: 'PRESENTACION_PREPARADA'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 2528, 1504 ], "id": "d7e6d879-f330-4944-a006-aa5735fe0177", "name": "Code - Preparar copia presentación ejecución TEST" }, { "parameters": { "operation": "copy", "fileId": { "__rl": true, "value": "={{ $json.slides_template_id }}", "mode": "id" }, "name": "={{ $json.presentation_name }}", "sameFolder": false, "driveId": { "__rl": true, "mode": "list", "value": "My Drive" }, "folderId": { "__rl": true, "value": "={{ $json.execution_folder_id }}", "mode": "id" }, "options": {} }, "type": "n8n-nodes-base.googleDrive", "typeVersion": 3, "position": [ 4448, 1552 ], "id": "f3543da5-0b8b-4de9-a934-8ed6daaa849c", "name": "Drive - Copiar plantilla presentación ejecución TEST", "credentials": { "googleDriveOAuth2Api": { "id": "g23xdGLZRzBGqKgH", "name": "Isaac - Google Drive" } } }, { "parameters": { "jsCode": "const base = $('Code - Preparar copia presentación ejecución TEST').first().json || {};\nconst drive = $json || {};\n\nconst presentationId =\n drive.id ||\n drive.fileId ||\n drive.presentationId ||\n drive.data?.id ||\n '';\n\nif (!presentationId) {\n throw new Error('No se encontró el ID de la presentación copiada.');\n}\n\nconst presentationUrl =\n drive.webViewLink ||\n drive.webUrl ||\n `https://docs.google.com/presentation/d/${presentationId}/edit`;\n\nreturn [\n {\n json: {\n ...base,\n\n presentation_id: presentationId,\n presentacion_ejecucion_url: presentationUrl,\n\n estado_presentacion: 'PRESENTACION_CREADA'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 4720, 1552 ], "id": "1b0f80c2-16cd-499a-aadf-3f44c3753ce4", "name": "Code - Normalizar link presentación ejecución TEST" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 737153956, "mode": "list", "cachedResultName": "propuestas_ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=737153956" }, "columns": { "mappingMode": "defineBelow", "value": { "presentacion_ejecucion_url": "={{ $json.presentacion_ejecucion_url }}", "ultima_actualizacion": "={{ new Date().toISOString() }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "session_id": "={{ $json.session_id }}", "media_folder_url": "={{ $json.media_folder_url }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_recepcion", "displayName": "fecha_recepcion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_ejecucion", "displayName": "fecha_ejecucion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_referencia", "displayName": "propuesta_referencia", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_match_estado", "displayName": "propuesta_match_estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_match_confianza", "displayName": "propuesta_match_confianza", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_nombre_banco", "displayName": "propuesta_nombre_banco", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_link_banco", "displayName": "propuesta_link_banco", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "marca", "displayName": "marca", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "cliente", "displayName": "cliente", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "pais", "displayName": "pais", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ubicacion", "displayName": "ubicacion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "comentario_original", "displayName": "comentario_original", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "resumen_ia", "displayName": "resumen_ia", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "descripcion_ejecucion", "displayName": "descripcion_ejecucion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "elementos_detectados", "displayName": "elementos_detectados", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "tags", "displayName": "tags", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_folder_url", "displayName": "media_folder_url", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "presentacion_ejecucion_url", "displayName": "presentacion_ejecucion_url", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fotos_count", "displayName": "fotos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audios_count", "displayName": "audios_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado_revision", "displayName": "estado_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actualizacion", "displayName": "ultima_actualizacion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "transcripcion_audio", "displayName": "transcripcion_audio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_match_revision", "displayName": "propuesta_match_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ 6480, 1424 ], "id": "cb35219a-46a0-49c2-a404-91c5c91a30d2", "name": "Sheets - Actualizar link presentación ejecución TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const data = $json || {};\n\nconst limpiarTexto = (valor, fallback = 'No identificado') => {\n const texto = String(valor ?? '').trim();\n\n if (!texto) return fallback;\n if (texto.toLowerCase() === 'undefined') return fallback;\n if (texto.toLowerCase() === 'null') return fallback;\n\n return texto;\n};\n\nconst cortar = (valor, max = 900) => {\n const texto = limpiarTexto(valor, 'No disponible');\n return texto.length > max ? texto.slice(0, max - 3) + '...' : texto;\n};\n\nconst normalizarTipoReporte = (valor) => {\n const texto = limpiarTexto(valor, '')\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toUpperCase()\n .replace(/\\s+/g, '_');\n\n if (\n texto === 'PROPUESTA_EXTERNA' ||\n texto === 'EXTERNA' ||\n texto === 'REPORTE_EXTERNO'\n ) {\n return 'PROPUESTA_EXTERNA';\n }\n\n if (\n texto === 'PROPUESTA_EJECUTADA' ||\n texto === 'EJECUTADA' ||\n texto === 'REPORTE_EJECUTADO'\n ) {\n return 'PROPUESTA_EJECUTADA';\n }\n\n return 'PROPUESTA_EJECUTADA';\n};\n\nconst tipoReporte = normalizarTipoReporte(\n data.tipo_reporte ||\n data.tipo_reporte_detectado ||\n data.reporte_tipo ||\n data.clasificacion_reporte ||\n ''\n);\n\nconst textoTipoReporte = tipoReporte === 'PROPUESTA_EXTERNA'\n ? 'Propuesta Externa'\n : 'Propuesta Ejecutada';\n\nconst tituloReporteTipo = tipoReporte === 'PROPUESTA_EXTERNA'\n ? 'REPORTE DE PROPUESTA EXTERNA'\n : 'REPORTE DE PROPUESTA EJECUTADA';\n\nconst encabezadoTipoReporte = `Fulgencio Fumado · ${textoTipoReporte}`;\n\nconst replacements = {\n '{{TITULO_REPORTE_TIPO}}': tituloReporteTipo,\n '{{ENCABEZADO_TIPO_REPORTE}}': encabezadoTipoReporte,\n '{{TIPO_REPORTE}}': textoTipoReporte,\n\n '{{MARCA}}': limpiarTexto(data.marca),\n '{{CLIENTE}}': limpiarTexto(data.cliente),\n '{{PAIS}}': limpiarTexto(data.pais),\n '{{UBICACION}}': limpiarTexto(data.ubicacion),\n\n '{{FECHA_EJECUCION}}': limpiarTexto(data.fecha_ejecucion),\n '{{PROPUESTA_REFERENCIA}}': limpiarTexto(data.propuesta_referencia),\n\n // Se dejan en blanco para no mostrar IDs técnicos en la presentación.\n // Los labels fijos \"ID de Ejecución\" / \"Ejecución ID\" deben eliminarse en la plantilla.\n '{{EJECUCION_ID}}': '',\n '{{SESSION_ID}}': '',\n\n '{{MANAGER_NOMBRE}}': limpiarTexto(data.manager_nombre),\n '{{MANAGER_TELEFONO}}': limpiarTexto(data.manager_telefono),\n\n '{{RESUMEN_IA}}': cortar(data.resumen_ia, 1000),\n '{{DESCRIPCION_EJECUCION}}': cortar(data.descripcion_ejecucion, 1000),\n '{{ELEMENTOS_DETECTADOS}}': cortar(data.elementos_detectados, 1000),\n '{{TAGS}}': cortar(data.tags, 500),\n\n '{{FOTOS_COUNT}}': limpiarTexto(data.fotos_count, '0'),\n '{{VIDEOS_COUNT}}': limpiarTexto(data.videos_count, '0'),\n '{{AUDIOS_COUNT}}': limpiarTexto(data.audios_count, '0'),\n\n '{{MEDIA_FOLDER_URL}}': limpiarTexto(data.media_folder_url, 'No disponible'),\n '{{LINK_PRESENTACION}}': limpiarTexto(data.presentacion_ejecucion_url, 'No disponible')\n};\n\nconst slidesReplaceRequests = Object.entries(replacements).map(([placeholder, value]) => {\n return {\n replaceAllText: {\n containsText: {\n text: placeholder,\n matchCase: true\n },\n replaceText: value\n }\n };\n});\n\nif (!data.presentation_id) {\n throw new Error('No llegó presentation_id para reemplazar textos en Slides.');\n}\n\nreturn [\n {\n json: {\n ...data,\n\n tipo_reporte_normalizado: tipoReporte,\n tipo_reporte_texto: textoTipoReporte,\n titulo_reporte_tipo: tituloReporteTipo,\n encabezado_tipo_reporte: encabezadoTipoReporte,\n\n slides_replacements: replacements,\n slides_replace_requests: slidesReplaceRequests,\n\n estado_presentacion: 'REEMPLAZOS_SLIDES_PREPARADOS'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 4928, 1552 ], "id": "72e00ab4-ed9e-4852-a89f-fdef9ba6551d", "name": "Code - Preparar reemplazos Slides ejecución TEST" }, { "parameters": { "method": "POST", "url": "={{ 'https://slides.googleapis.com/v1/presentations/' + $json.presentation_id + ':batchUpdate' }}", "authentication": "predefinedCredentialType", "nodeCredentialType": "googleOAuth2Api", "sendBody": true, "specifyBody": "json", "jsonBody": "={{ { requests: $json.slides_replace_requests } }}", "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ 5616, 1536 ], "id": "447d49f8-bac0-4c12-bbd0-89dbd2a43b2c", "name": "HTTP Request - Reemplazar textos Slides ejecución TEST", "credentials": { "googleOAuth2Api": { "id": "eHseMeH39kRcXgOF", "name": "Google account 2" } } }, { "parameters": { "jsCode": "const base = $('Code - Preparar reemplazos Slides ejecución TEST').first().json || {};\nconst respuestaSlides = $json || {};\n\nreturn [\n {\n json: {\n ...base,\n\n slides_batch_update_response: respuestaSlides,\n estado_presentacion: 'TEXTOS_SLIDES_REEMPLAZADOS'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 6224, 1536 ], "id": "9c252b51-c657-4020-bae8-fd79824feb32", "name": "Code - Normalizar respuesta Slides ejecución TEST" }, { "parameters": { "jsCode": "const actual = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst base = getNodeJson('Code - Resolver sesión WhatsApp TEST');\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst idioma = limpiar(\n actual.idioma_flujo ||\n base.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst destinoBase = limpiar(\n base.whatsapp_to ||\n base.group_jid ||\n actual.whatsapp_to ||\n actual.group_jid ||\n base.whatsapp_remote_jid ||\n actual.whatsapp_remote_jid ||\n base.manager_telefono ||\n actual.manager_telefono ||\n base.sender_phone ||\n actual.sender_phone ||\n ''\n);\n\nif (!destinoBase) {\n throw new Error('No se encontró destino WhatsApp para enviar mensaje de cancelación sin sesión.');\n}\n\nlet whatsappTo = destinoBase;\n\nif (whatsappTo.includes('@g.us')) {\n whatsappTo = whatsappTo;\n} else if (whatsappTo.includes('@s.whatsapp.net')) {\n whatsappTo = whatsappTo;\n} else {\n whatsappTo = `${whatsappTo.replace(/\\D/g, '')}@s.whatsapp.net`;\n}\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n 'ℹ️ *There is no active report to cancel.*',\n '',\n 'You can start a new evidence registration whenever you are ready.',\n '',\n 'Write:',\n '',\n '*Hey*'\n ].join('\\n');\n} else {\n mensaje = [\n 'ℹ️ *No hay un reporte activo para cancelar.*',\n '',\n 'Puedes iniciar un nuevo registro de evidencias cuando estés listo.',\n '',\n 'Escribe:',\n '',\n '*Hey*'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...base,\n ...actual,\n\n whatsapp_to: whatsappTo,\n whatsapp_text: mensaje\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5440, -1792 ], "id": "be56aeb8-916d-46b2-b2a2-b544a4bdbd01", "name": "Code - Preparar sin sesión para cancelar WhatsApp TEST" }, { "parameters": { "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 306129743, "mode": "list", "cachedResultName": "wa_ejecuciones_eventos", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=306129743" }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -5392, -16 ], "id": "cc41848d-df63-499a-ba01-16155e30fffe", "name": "Sheets - Leer eventos sesión fotos WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const data = $json || {};\n\nconst limpiarTexto = (valor, fallback = 'No identificado') => {\n const texto = String(valor ?? '').trim();\n\n if (!texto) return fallback;\n if (texto.toLowerCase() === 'undefined') return fallback;\n if (texto.toLowerCase() === 'null') return fallback;\n\n return texto;\n};\n\nconst limpiarNombreArchivo = (valor) => {\n return limpiarTexto(valor)\n .replace(/[\\\\/:*?\"<>|#%{}~&]/g, '-')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, 120);\n};\n\nconst fechaBase = limpiarTexto(\n data.fecha_ejecucion ||\n data.fecha_recepcion ||\n data.ultima_actualizacion ||\n new Date().toISOString()\n);\n\nconst fecha = fechaBase.slice(0, 10);\n\nconst marca = limpiarNombreArchivo(data.marca);\nconst cliente = limpiarNombreArchivo(data.cliente);\nconst sessionId = limpiarNombreArchivo(data.session_id || data.ejecucion_id);\n\nconst folderName = `${fecha} - ${marca} - ${cliente} - ${sessionId}`;\n\n// Carpeta base PROPUESTAS EJECUTADAS\nconst parentFolderId = '1q92b3lMw_fcjD1YUtp5YcJKne49zOfpc';\n\nreturn [\n {\n json: {\n ...data,\n\n execution_folder_name: folderName,\n execution_parent_folder_id: parentFolderId\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 2736, 1504 ], "id": "3a7cf341-e7e6-4d90-bdd0-9e1abde12b10", "name": "Code - Preparar carpeta ejecución TEST" }, { "parameters": { "resource": "folder", "name": "={{ $json.execution_folder_name }}", "driveId": { "__rl": true, "mode": "list", "value": "My Drive" }, "folderId": { "__rl": true, "value": "={{ $json.execution_parent_folder_id }}", "mode": "id" }, "options": {} }, "type": "n8n-nodes-base.googleDrive", "typeVersion": 3, "position": [ 2944, 1504 ], "id": "ba2e9788-c593-4097-96be-e7f6d447be69", "name": "Drive - Crear carpeta ejecución TEST", "credentials": { "googleDriveOAuth2Api": { "id": "g23xdGLZRzBGqKgH", "name": "Isaac - Google Drive" } } }, { "parameters": { "jsCode": "const carpeta = $json || {};\nconst base = $('Code - Preparar carpeta ejecución TEST').first().json || {};\n\nconst folderId =\n carpeta.id ||\n carpeta.fileId ||\n carpeta.folderId ||\n '';\n\nconst folderUrl = folderId\n ? `https://drive.google.com/drive/folders/${folderId}`\n : '';\n\nif (!folderId) {\n throw new Error('No se pudo obtener el ID de la carpeta de ejecución.');\n}\n\nreturn [\n {\n json: {\n ...base,\n\n execution_folder_id: folderId,\n execution_folder_url: folderUrl,\n\n media_folder_id: folderId,\n media_folder_url: folderUrl\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 3152, 1504 ], "id": "3861110f-3334-4e89-954f-b8d86f9b4708", "name": "Code - Normalizar carpeta ejecución TEST" }, { "parameters": { "jsCode": "const data = $json || {};\n\nconst texto = (valor) => String(valor ?? '').trim();\n\nconst folderId = texto(data.execution_folder_id || data.media_folder_id);\n\nif (!folderId) {\n throw new Error('No existe execution_folder_id/media_folder_id para mover la multimedia.');\n}\n\nconst archivos = [];\nconst vistos = new Set();\n\nconst extraerFileIdDesdeUrl = (valor) => {\n const url = texto(valor);\n if (!url) return '';\n\n // Formato: https://drive.google.com/file/d/FILE_ID/view\n const matchFile = url.match(/\\/file\\/d\\/([^/]+)/);\n if (matchFile?.[1]) return matchFile[1];\n\n // Formato: https://drive.google.com/open?id=FILE_ID\n const matchOpen = url.match(/[?&]id=([^&]+)/);\n if (matchOpen?.[1]) return matchOpen[1];\n\n // Si ya viene como ID limpio\n if (!url.includes('http') && url.length > 15) return url;\n\n return '';\n};\n\nconst separarLista = (valor) => {\n return texto(valor)\n .split(/[\\n,]+/)\n .map(v => texto(v))\n .filter(Boolean);\n};\n\nconst agregarArchivo = (fileIdOrUrl, tipo) => {\n const fileId = extraerFileIdDesdeUrl(fileIdOrUrl);\n\n if (!fileId) return;\n if (vistos.has(fileId)) return;\n\n vistos.add(fileId);\n\n archivos.push({\n json: {\n ...data,\n\n move_file_id: fileId,\n move_file_type: tipo,\n\n execution_folder_id: folderId,\n media_folder_id: folderId,\n media_folder_url: data.media_folder_url,\n execution_folder_url: data.execution_folder_url\n }\n });\n};\n\n// 1. Archivo actual del item, si existe\nagregarArchivo(data.drive_file_id, data.media_type || 'media');\n\n// 2. URLs acumuladas por tipo\nfor (const url of separarLista(data.audio_drive_urls)) {\n agregarArchivo(url, 'audio');\n}\n\nfor (const url of separarLista(data.imagenes_drive_urls)) {\n agregarArchivo(url, 'image');\n}\n\nfor (const url of separarLista(data.videos_drive_urls)) {\n agregarArchivo(url, 'video');\n}\n\n// 3. Si tienes otros campos futuros\nfor (const url of separarLista(data.media_drive_urls)) {\n agregarArchivo(url, 'media');\n}\n\nif (archivos.length === 0) {\n return [\n {\n json: {\n ...data,\n media_move_status: 'SIN_ARCHIVOS_PARA_MOVER',\n media_move_debug: {\n drive_file_id: data.drive_file_id || '',\n audio_drive_urls: data.audio_drive_urls || '',\n imagenes_drive_urls: data.imagenes_drive_urls || '',\n videos_drive_urls: data.videos_drive_urls || ''\n }\n }\n }\n ];\n}\n\nreturn archivos;" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 3360, 1504 ], "id": "1ccc2d93-f72d-453a-a1b8-7a204d5a5108", "name": "Code - Preparar media para mover carpeta TEST" }, { "parameters": { "operation": "move", "fileId": { "__rl": true, "value": "={{ $json.move_file_id }}", "mode": "id" }, "driveId": { "__rl": true, "mode": "list", "value": "My Drive" }, "folderId": { "__rl": true, "value": "={{ $json.execution_folder_id }}", "mode": "id" } }, "type": "n8n-nodes-base.googleDrive", "typeVersion": 3, "position": [ 3568, 1504 ], "id": "fa4bc0cf-498d-4332-8d12-72f4eb7e5ce4", "name": "Drive - Mover media a carpeta ejecución TEST", "credentials": { "googleDriveOAuth2Api": { "id": "g23xdGLZRzBGqKgH", "name": "Isaac - Google Drive" } } }, { "parameters": { "jsCode": "const movido = $json || {};\nconst base = $('Code - Preparar media para mover carpeta TEST').first().json || {};\n\nreturn [\n {\n json: {\n ...base,\n\n media_move_status: 'MEDIA_MOVIDA_A_CARPETA',\n moved_file_id: base.move_file_id,\n moved_file_type: base.move_file_type,\n moved_file_url: base.move_file_url,\n\n drive_move_response_id: movido.id || movido.fileId || base.move_file_id\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 3776, 1504 ], "id": "2b0b6839-7d9b-4dc1-9969-a5b3b015653e", "name": "Code - Confirmar media movida carpeta TEST" }, { "parameters": { "jsCode": "const actual = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst base = getNodeJson('Code - Preparar reemplazos Slides ejecución TEST');\n\nconst data = {\n ...base,\n ...actual,\n\n // Aseguramos estos campos desde el nodo base si el nodo Slides no los devuelve\n presentation_id: actual.presentation_id || base.presentation_id,\n imagenes_drive_urls: actual.imagenes_drive_urls || base.imagenes_drive_urls,\n imagen_drive_urls: actual.imagen_drive_urls || base.imagen_drive_urls,\n media_drive_urls: actual.media_drive_urls || base.media_drive_urls,\n};\n\nconst texto = (valor) => String(valor ?? '').trim();\n\nconst extraerFileIdDesdeUrl = (valor) => {\n const url = texto(valor);\n if (!url) return '';\n\n const matchFile = url.match(/\\/file\\/d\\/([^/]+)/);\n if (matchFile?.[1]) return matchFile[1];\n\n const matchOpen = url.match(/[?&]id=([^&]+)/);\n if (matchOpen?.[1]) return matchOpen[1];\n\n if (!url.includes('http') && url.length > 15) return url;\n\n return '';\n};\n\nconst separarLista = (valor) => {\n return texto(valor)\n .split(/[\\n,]+/)\n .map(v => texto(v))\n .filter(Boolean);\n};\n\nconst imagenesUrls = separarLista(\n data.imagenes_drive_urls ||\n data.imagen_drive_urls ||\n data.media_drive_urls ||\n ''\n);\n\nconst imageFileIds = imagenesUrls\n .map(extraerFileIdDesdeUrl)\n .filter(Boolean)\n .slice(0, 6);\n\nif (!data.presentation_id) {\n throw new Error('No llegó presentation_id para insertar imágenes en Slides.');\n}\n\nreturn [\n {\n json: {\n ...data,\n\n apps_script_insert_images_payload: {\n secret: 'glm_fulgencio_slides_2026_seguro',\n presentation_id: data.presentation_id,\n image_file_ids: imageFileIds\n },\n\n imagenes_para_slides_count: imageFileIds.length,\n imagenes_para_slides_ids: imageFileIds,\n\n estado_insertar_imagenes: 'PAYLOAD_INSERTAR_IMAGENES_PREPARADO'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 5824, 1536 ], "id": "2606aa33-73fd-4af3-9497-cea6932760c9", "name": "Code - Preparar payload insertar imágenes Slides TEST" }, { "parameters": { "method": "POST", "url": "https://script.google.com/macros/s/AKfycbyw76HYjYMvq0KXA9IN5S8nRWk0drPkjKSK2SAbCL9Ha8is3vFZMyR6Ldby4c3YVs7J/exec", "sendBody": true, "specifyBody": "json", "jsonBody": "={{ $json.apps_script_insert_images_payload }}", "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ 6032, 1536 ], "id": "4ed89dd2-2849-419e-95e4-e061939e0002", "name": "HTTP - Insertar imágenes en Slides TEST" }, { "parameters": { "jsCode": "const actual = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst base = getNodeJson('Code - Resolver paso activo WhatsApp TEST');\nconst sesion = base.sesion_activa || actual.sesion_activa || {};\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst normalizarDestino = (valor) => {\n let raw = limpiar(valor);\n\n if (!raw) return '';\n\n raw = raw.replace('@c.us', '@s.whatsapp.net');\n\n if (raw.includes('@g.us')) return raw;\n\n if (raw.includes('@s.whatsapp.net')) {\n const numero = raw.replace('@s.whatsapp.net', '').replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n }\n\n const numero = raw.replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n};\n\nconst idioma = limpiar(\n actual.idioma_flujo ||\n base.idioma_flujo ||\n sesion.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst destinoBase =\n actual.whatsapp_to ||\n actual.group_jid ||\n actual.whatsapp_remote_jid ||\n base.whatsapp_to ||\n base.group_jid ||\n base.whatsapp_remote_jid ||\n sesion.group_jid ||\n sesion.whatsapp_to ||\n sesion.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n actual.manager_telefono ||\n base.sender_phone ||\n actual.sender_phone ||\n '';\n\nif (!destinoBase) {\n throw new Error('No se encontró destino WhatsApp para enviar aviso de procesamiento.');\n}\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '⏳ *Report received.*',\n '',\n 'We are now processing your evidence.',\n '',\n 'This may take a moment while we analyze the media, organize the files and generate the presentation.',\n '',\n 'I will send you the final link when everything is ready.'\n ].join('\\n');\n} else {\n mensaje = [\n '⏳ *Reporte recibido.*',\n '',\n 'Estamos procesando tus evidencias.',\n '',\n 'Esto puede tomar un momento mientras analizamos los archivos, organizamos la carpeta y generamos la presentación.',\n '',\n 'Te enviaré el link final cuando todo esté listo.'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...base,\n ...actual,\n\n whatsapp_to: normalizarDestino(destinoBase),\n whatsapp_text: mensaje,\n\n aviso_procesando_preparado: true\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5936, 688 ], "id": "fdbbc5d5-a162-4bfa-ab41-47f4087dba08", "name": "Code - Preparar aviso procesando WhatsApp TEST1" }, { "parameters": { "jsCode": "const respuesta = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nlet base =\n getNodeJson('Code - Preparar aviso procesando WhatsApp TEST') ||\n getNodeJson('Code - Preparar aviso procesando WhatsApp TEST1') ||\n {};\n\nif (!base || Object.keys(base).length === 0) {\n base = getNodeJson('Code - Resolver paso activo WhatsApp TEST') || {};\n}\n\nreturn [\n {\n json: {\n ...base,\n\n aviso_procesando_enviado: true,\n aviso_procesando_response: respuesta\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5520, 688 ], "id": "78fdf65a-3ff9-4219-b22f-9088baa65aeb", "name": "Code - Restaurar contexto aviso procesando TEST" }, { "parameters": { "method": "POST", "url": "={{'https://wsp.gomezleemarketing.com'}}/message/sendText/{{'botsoporte'}}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "apikey", "value": "={{'C267126ABB45-4C12-B626-6BAB1833F5D7'}}" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ { "name": "number", "value": "={{ (() => {\n const limpiar = (valor) => String(valor ?? '').trim();\n\n const candidatos = [\n $json.whatsapp_to,\n $json.group_jid,\n $json.whatsapp_remote_jid,\n $json.sesion_activa?.group_jid,\n $json.sesion_activa?.whatsapp_to,\n $json.sesion_activa?.whatsapp_remote_jid,\n $json.manager_telefono,\n $json.sender_phone\n ];\n\n let raw = limpiar(candidatos.find(v => limpiar(v)));\n\n if (!raw) return '';\n\n raw = raw.replace('@c.us', '@s.whatsapp.net');\n\n // Si ya viene como grupo, conservarlo.\n if (raw.includes('@g.us')) {\n return raw;\n }\n\n // Si viene como usuario WhatsApp, validar si realmente era un grupo.\n if (raw.includes('@s.whatsapp.net')) {\n const numero = raw.replace('@s.whatsapp.net', '').replace(/\\D/g, '');\n\n // Los grupos de WhatsApp suelen venir como 120363...\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n }\n\n const numero = raw.replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n})() }}" }, { "name": "delay", "value": "={{ 1000 }}" }, { "name": "text", "value": "={{ $json.whatsapp_text }}" } ] }, "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ -5744, 688 ], "id": "eeb80d93-128f-488e-814e-86170b8049f7", "name": "HTTP - Enviar aviso procesando WhatsApp TEST" }, { "parameters": { "documentId": { "__rl": true, "value": "1iy3lC3DS3ZfShb7d26iQOrPjx9s_3B-PC07XfYew7ng", "mode": "list", "cachedResultName": "BANCO DE PROPUESTAS DE CDC PARA FULGENCIO FUMADO", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iy3lC3DS3ZfShb7d26iQOrPjx9s_3B-PC07XfYew7ng/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": "gid=0", "mode": "list", "cachedResultName": "propuestas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iy3lC3DS3ZfShb7d26iQOrPjx9s_3B-PC07XfYew7ng/edit#gid=0" }, "options": { "dataLocationOnSheet": { "values": { "rangeDefinition": "detectAutomatically" } }, "outputFormatting": { "values": { "general": "UNFORMATTED_VALUE", "date": "FORMATTED_STRING" } }, "returnFirstMatch": false } }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -208, 1360 ], "id": "0eb5f707-d1a2-4147-a195-a88ed9b5903e", "name": "Sheets - Leer banco propuestas Fulgencio TEST", "alwaysOutputData": true, "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst propuesta =\n getNodeJson('Code - Normalizar JSON final propuesta ejecutada TEST') ||\n $json ||\n {};\n\nconst bancoRows = $input.all().map(item => item.json || {});\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst normalizar = (valor) => {\n return limpiar(valor)\n .replace(/\\+/g, ' mas ')\n .replace(/_/g, ' ')\n .replace(/-/g, ' ')\n .replace(/\\//g, ' ')\n .replace(/\\./g, ' ')\n .replace(/\\bGUATE\\b/gi, ' Guatemala ')\n .replace(/\\bGUA\\b/gi, ' Guatemala ')\n .replace(/\\bGT\\b/gi, ' Guatemala ')\n .replace(/\\bRD\\b/gi, ' Republica Dominicana ')\n .replace(/\\bREP DOM\\b/gi, ' Republica Dominicana ')\n .replace(/\\bREP DOMINICANA\\b/gi, ' Republica Dominicana ')\n .replace(/\\bDOMINICANA\\b/gi, ' Republica Dominicana ')\n .replace(/\\bCR\\b/gi, ' Costa Rica ')\n .replace(/\\bPAN\\b/gi, ' Panama ')\n .replace(/\\bPTY\\b/gi, ' Panama ')\n .replace(/\\bSV\\b/gi, ' El Salvador ')\n .replace(/\\bSALVADOR\\b/gi, ' El Salvador ')\n .replace(/\\bHND\\b/gi, ' Honduras ')\n .replace(/\\bNIC\\b/gi, ' Nicaragua ')\n .replace(/\\bWMC\\b/gi, ' WMC ')\n .replace(/\\bRTM\\b/gi, ' RTM ')\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9\\s]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n};\n\nconst tokens = (valor) => {\n const stopwords = new Set([\n 'de', 'del', 'la', 'el', 'los', 'las', 'y', 'en', 'para', 'por', 'con',\n 'una', 'un', 'uno', 'dos', 'tres', 'the', 'of', 'and', 'for', 'to', 'a',\n 'propuesta', 'presentacion', 'presentación', 'ejecucion', 'ejecución',\n 'ejecutada', 'ejecutado', 'reporte', 'evidencia', 'version', 'versión',\n 'archivo', 'link', 'slide', 'slides', 'prueba'\n ]);\n\n return normalizar(valor)\n .split(' ')\n .map(t => t.trim())\n .filter(Boolean)\n .filter(t => t.length >= 3 || /^v\\d+$/i.test(t))\n .filter(t => !stopwords.has(t));\n};\n\nconst tokenSet = (valor) => new Set(tokens(valor));\n\nconst similitudTokens = (a, b) => {\n const ta = tokenSet(a);\n const tb = tokenSet(b);\n\n if (ta.size === 0 || tb.size === 0) return 0;\n\n let interseccion = 0;\n\n for (const token of ta) {\n if (tb.has(token)) interseccion++;\n }\n\n const union = new Set([...ta, ...tb]).size;\n\n return union === 0 ? 0 : interseccion / union;\n};\n\nconst coberturaTokens = (textoBase, textoContra) => {\n const base = [...tokenSet(textoBase)];\n const contra = tokenSet(textoContra);\n\n if (base.length === 0) return 0;\n\n const encontrados = base.filter(t => contra.has(t));\n\n return encontrados.length / base.length;\n};\n\nconst contarTokensComunes = (a, b) => {\n const ta = tokenSet(a);\n const tb = tokenSet(b);\n\n let comunes = 0;\n\n for (const token of ta) {\n if (tb.has(token)) comunes++;\n }\n\n return comunes;\n};\n\nconst obtenerCampo = (row, posiblesNombres) => {\n const keys = Object.keys(row || {});\n\n for (const nombre of posiblesNombres) {\n const exacto = keys.find(k => normalizar(k) === normalizar(nombre));\n if (exacto && limpiar(row[exacto])) return limpiar(row[exacto]);\n }\n\n for (const nombre of posiblesNombres) {\n const parcial = keys.find(k => normalizar(k).includes(normalizar(nombre)));\n if (parcial && limpiar(row[parcial])) return limpiar(row[parcial]);\n }\n\n return '';\n};\n\nconst extraerVersion = (valor) => {\n const texto = normalizar(valor);\n const match = texto.match(/\\bv\\d+\\b/);\n return match ? match[0].toUpperCase() : '';\n};\n\nconst extraerAnio = (valor) => {\n const texto = limpiar(valor);\n const match = texto.match(/\\b20\\d{2}\\b/);\n return match ? match[0] : '';\n};\n\nconst contienePais = (texto, pais) => {\n const t = normalizar(texto);\n const p = normalizar(pais);\n\n if (!p) return false;\n\n if (t.includes(p)) return true;\n\n if (p.includes('guatemala') && t.includes('guatemala')) return true;\n if (p.includes('republica dominicana') && t.includes('republica dominicana')) return true;\n if (p.includes('costa rica') && t.includes('costa rica')) return true;\n if (p.includes('panama') && t.includes('panama')) return true;\n if (p.includes('salvador') && t.includes('salvador')) return true;\n if (p.includes('honduras') && t.includes('honduras')) return true;\n if (p.includes('nicaragua') && t.includes('nicaragua')) return true;\n\n return false;\n};\n\nconst tipoReporte = limpiar(propuesta.tipo_reporte).toUpperCase();\n\nif (tipoReporte !== 'PROPUESTA_EJECUTADA') {\n return [\n {\n json: {\n ...propuesta,\n\n propuesta_match_estado:\n tipoReporte === 'PROPUESTA_EXTERNA'\n ? 'NO_APLICA_PROPUESTA_EXTERNA'\n : 'NO_APLICA_NO_DETERMINADO',\n\n propuesta_match_confianza: '0',\n propuesta_nombre_banco: '',\n propuesta_link_banco: '',\n propuesta_banco_row_number: '',\n propuesta_banco_enlaces_existentes: '',\n propuesta_banco_file_id: '',\n\n propuesta_match_revision:\n tipoReporte === 'PROPUESTA_EXTERNA'\n ? 'NO_REQUIERE_MATCH_BANCO'\n : 'REQUIERE_REVISION_TIPO_NO_DETERMINADO',\n\n match_banco_debug: {\n motivo: 'No se ejecutó match porque el tipo_reporte no es PROPUESTA_EJECUTADA.',\n tipo_reporte: tipoReporte || 'NO_DETERMINADO'\n }\n }\n }\n ];\n}\n\nconst referencia = limpiar(propuesta.propuesta_referencia);\nconst marca = limpiar(propuesta.marca);\nconst cliente = limpiar(propuesta.cliente);\nconst pais = limpiar(propuesta.pais);\nconst ubicacion = limpiar(propuesta.ubicacion);\nconst descripcion = limpiar(propuesta.descripcion_ejecucion || propuesta.resumen_ia);\nconst elementosDetectados = limpiar(propuesta.elementos_detectados);\nconst comentarioOriginal = limpiar(propuesta.comentario_original);\nconst transcripcionAudio = limpiar(propuesta.transcripcion_audio);\nconst textoAudio = limpiar(propuesta.texto_audio);\nconst textoImagenes = limpiar(propuesta.texto_imagenes);\nconst tags = limpiar(propuesta.tags);\n\nconst textoPropuestaCompleto = [\n referencia,\n marca,\n cliente,\n pais,\n ubicacion,\n descripcion,\n elementosDetectados,\n comentarioOriginal,\n transcripcionAudio,\n textoAudio,\n textoImagenes,\n tags\n].filter(Boolean).join(' ');\n\nconst textoClavePropuesta = [\n referencia,\n marca,\n cliente,\n pais,\n elementosDetectados,\n textoImagenes,\n tags\n].filter(Boolean).join(' ');\n\nconst versionPropuesta = extraerVersion(textoPropuestaCompleto);\nconst anioPropuesta = extraerAnio(textoPropuestaCompleto);\n\nconst candidatos = [];\n\nfor (const [index, row] of bancoRows.entries()) {\n const bancoRowNumber = String(index + 2);\n\n const bancoNombre = obtenerCampo(row, [\n 'NOMBRE',\n 'propuesta',\n 'nombre propuesta',\n 'nombre de propuesta',\n 'titulo',\n 'título',\n 'nombre',\n 'referencia',\n 'brief',\n 'idea'\n ]);\n\n const bancoTipoAccion = obtenerCampo(row, [\n 'TIPO DE ACCION',\n 'TIPO DE ACCIÓN',\n 'tipo accion',\n 'tipo acción'\n ]);\n\n const bancoCliente = obtenerCampo(row, [\n 'CLIENTE',\n 'cliente',\n 'client'\n ]);\n\n const bancoMarca = obtenerCampo(row, [\n 'MARCA',\n 'marca',\n 'brand'\n ]);\n\n const bancoPais = obtenerCampo(row, [\n 'PAIS',\n 'PAÍS',\n 'pais',\n 'país',\n 'country'\n ]);\n\n const bancoCanal = obtenerCampo(row, [\n 'CANAL',\n 'canal'\n ]);\n\n const bancoAmbiente = obtenerCampo(row, [\n 'AMBIENTE DE COMPRA',\n 'ambiente de compra',\n 'ambiente'\n ]);\n\n const bancoDescripcion = obtenerCampo(row, [\n 'Descripcion',\n 'Descripción',\n 'descripcion',\n 'descripción',\n 'detalle',\n 'mecanica',\n 'mecánica',\n 'comentario',\n 'observacion',\n 'observación'\n ]);\n\n const bancoLink = obtenerCampo(row, [\n 'Enlace a la propuesta',\n 'link',\n 'enlace',\n 'url',\n 'brief link',\n 'link brief',\n 'presentacion',\n 'presentación',\n 'drive'\n ]);\n\n const bancoFileId = obtenerCampo(row, [\n 'file_id',\n 'File ID',\n 'archivo_id'\n ]);\n\n const bancoEnlacesEjecutadas = obtenerCampo(row, [\n 'Enlaces a propuestas ejecutadas',\n 'Enlace a propuestas ejecutadas',\n 'Links propuestas ejecutadas',\n 'Link propuestas ejecutadas',\n 'Ejecuciones',\n 'Propuestas ejecutadas'\n ]);\n\n if (!bancoNombre && !bancoMarca && !bancoCliente && !bancoDescripcion) {\n continue;\n }\n\n const textoBancoCompleto = [\n bancoNombre,\n bancoTipoAccion,\n bancoCliente,\n bancoMarca,\n bancoPais,\n bancoCanal,\n bancoAmbiente,\n bancoDescripcion\n ].filter(Boolean).join(' ');\n\n const textoClaveBanco = [\n bancoNombre,\n bancoCliente,\n bancoMarca,\n bancoPais\n ].filter(Boolean).join(' ');\n\n const scoreReferenciaNombre = similitudTokens(referencia, bancoNombre);\n const scoreEvidenciaNombre = similitudTokens(textoClavePropuesta, bancoNombre);\n const coberturaNombreEnEvidencia = coberturaTokens(bancoNombre, textoPropuestaCompleto);\n const coberturaReferenciaEnBanco = coberturaTokens(referencia, textoBancoCompleto);\n\n const scoreNombre = Math.max(\n scoreReferenciaNombre,\n scoreEvidenciaNombre,\n coberturaNombreEnEvidencia,\n coberturaReferenciaEnBanco\n );\n\n const scoreMarcaContraBanco = Math.max(\n similitudTokens(marca, bancoMarca),\n similitudTokens(marca, bancoNombre),\n similitudTokens(marca, textoBancoCompleto)\n );\n\n const scoreClienteContraBanco = Math.max(\n similitudTokens(cliente, bancoCliente),\n similitudTokens(cliente, bancoNombre),\n similitudTokens(cliente, textoBancoCompleto)\n );\n\n const scorePais = Math.max(\n similitudTokens(pais, bancoPais),\n contienePais(bancoNombre, pais) ? 1 : 0,\n contienePais(textoBancoCompleto, pais) ? 1 : 0\n );\n\n const scoreGeneral = similitudTokens(textoPropuestaCompleto, textoBancoCompleto);\n const scoreClave = similitudTokens(textoClavePropuesta, textoClaveBanco);\n const coberturaBancoEnPropuesta = coberturaTokens(bancoNombre, textoPropuestaCompleto);\n const comunesNombreEvidencia = contarTokensComunes(bancoNombre, textoPropuestaCompleto);\n\n const versionBanco = extraerVersion(textoBancoCompleto);\n const anioBanco = extraerAnio(textoBancoCompleto);\n\n const versionCoincide =\n versionPropuesta &&\n versionBanco &&\n versionPropuesta === versionBanco;\n\n const versionContradice =\n versionPropuesta &&\n versionBanco &&\n versionPropuesta !== versionBanco;\n\n const anioCoincide =\n anioPropuesta &&\n anioBanco &&\n anioPropuesta === anioBanco;\n\n const anioContradice =\n anioPropuesta &&\n anioBanco &&\n anioPropuesta !== anioBanco;\n\n const paisCoincide =\n scorePais >= 0.80 ||\n contienePais(bancoNombre, pais) ||\n contienePais(textoBancoCompleto, pais);\n\n const marcaCoincide =\n scoreMarcaContraBanco >= 0.45 ||\n normalizar(textoBancoCompleto).includes(normalizar(marca)) ||\n normalizar(textoPropuestaCompleto).includes(normalizar(bancoMarca));\n\n const scorePonderado =\n scoreNombre * 0.46 +\n scoreClave * 0.22 +\n scoreMarcaContraBanco * 0.08 +\n scoreClienteContraBanco * 0.04 +\n scorePais * 0.10 +\n scoreGeneral * 0.10;\n\n let scoreFinal = scorePonderado;\n let reglaMatch = 'PONDERADO';\n\n // Regla 1: el nombre/referencia de la propuesta está claramente contenido en la evidencia.\n if (coberturaBancoEnPropuesta >= 0.85 && (paisCoincide || anioCoincide || versionCoincide)) {\n scoreFinal = Math.max(scoreFinal, versionCoincide ? 0.97 : 0.93);\n reglaMatch = versionCoincide\n ? 'NOMBRE_BANCO_EN_EVIDENCIA_CON_VERSION'\n : 'NOMBRE_BANCO_EN_EVIDENCIA';\n }\n\n // Regla 2: combinación fuerte de clave propuesta-banco.\n if (scoreClave >= 0.85 && (paisCoincide || anioCoincide || marcaCoincide)) {\n scoreFinal = Math.max(scoreFinal, 0.95);\n reglaMatch = 'CLAVE_COMPLETA_PROPUESTA_BANCO';\n } else if (scoreClave >= 0.75 && (paisCoincide || versionCoincide)) {\n scoreFinal = Math.max(scoreFinal, 0.90);\n reglaMatch = 'CLAVE_FUERTE_PROPUESTA_BANCO';\n }\n\n // Regla 3: referencia parcial + país + versión.\n if (scoreNombre >= 0.60 && paisCoincide && versionCoincide) {\n scoreFinal = Math.max(scoreFinal, 0.94);\n reglaMatch = 'REFERENCIA_PAIS_VERSION';\n }\n\n // Regla 4: muchos tokens comunes, aunque Gemini haya escrito el nombre distinto.\n if (comunesNombreEvidencia >= 5 && (paisCoincide || anioCoincide)) {\n scoreFinal = Math.max(scoreFinal, versionCoincide ? 0.96 : 0.88);\n reglaMatch = versionCoincide\n ? 'TOKENS_COMUNES_PAIS_VERSION'\n : 'TOKENS_COMUNES_PAIS';\n }\n\n // Regla 5: para casos como RTM+ vs RTM más, GUATE vs Guatemala.\n if (\n normalizar(textoPropuestaCompleto).includes('rtm') &&\n normalizar(textoBancoCompleto).includes('rtm') &&\n normalizar(textoPropuestaCompleto).includes('wmc') &&\n normalizar(textoBancoCompleto).includes('wmc') &&\n (paisCoincide || normalizar(textoBancoCompleto).includes('guatemala')) &&\n (marcaCoincide || normalizar(textoBancoCompleto).includes('campero'))\n ) {\n scoreFinal = Math.max(scoreFinal, versionCoincide ? 0.96 : 0.89);\n reglaMatch = versionCoincide\n ? 'RTM_WMC_MARCA_PAIS_VERSION'\n : 'RTM_WMC_MARCA_PAIS';\n }\n\n // Penalizaciones de seguridad.\n if (anioContradice) {\n scoreFinal = Math.min(scoreFinal, 0.59);\n reglaMatch = `${reglaMatch}_PENALIZADO_ANIO`;\n }\n\n if (versionContradice && scoreFinal >= 0.85) {\n scoreFinal = Math.min(scoreFinal, 0.79);\n reglaMatch = `${reglaMatch}_PENALIZADO_VERSION`;\n }\n\n // Si no coincide ni país, ni marca, ni año, no lo dejamos como alta confianza.\n if (\n scoreFinal >= 0.85 &&\n !paisCoincide &&\n !marcaCoincide &&\n !anioCoincide\n ) {\n scoreFinal = 0.74;\n reglaMatch = `${reglaMatch}_PENALIZADO_SIN_ANCLA`;\n }\n\n let bonusOrden = 0;\n\n if (versionCoincide) bonusOrden += 0.04;\n if (anioCoincide) bonusOrden += 0.02;\n if (paisCoincide) bonusOrden += 0.02;\n if (marcaCoincide) bonusOrden += 0.01;\n if (bancoFileId) bonusOrden += 0.005;\n\n candidatos.push({\n row,\n score: scoreFinal,\n score_orden: scoreFinal + bonusOrden,\n score_ponderado: scorePonderado,\n score_porcentaje: Math.round(scoreFinal * 100),\n\n score_nombre: Math.round(scoreNombre * 100),\n score_referencia_nombre: Math.round(scoreReferenciaNombre * 100),\n score_evidencia_nombre: Math.round(scoreEvidenciaNombre * 100),\n cobertura_nombre_en_evidencia: Math.round(coberturaNombreEnEvidencia * 100),\n cobertura_referencia_en_banco: Math.round(coberturaReferenciaEnBanco * 100),\n score_marca: Math.round(scoreMarcaContraBanco * 100),\n score_cliente: Math.round(scoreClienteContraBanco * 100),\n score_pais: Math.round(scorePais * 100),\n score_general: Math.round(scoreGeneral * 100),\n score_clave: Math.round(scoreClave * 100),\n cobertura_banco_en_propuesta: Math.round(coberturaBancoEnPropuesta * 100),\n comunes_nombre_evidencia: comunesNombreEvidencia,\n\n version_propuesta: versionPropuesta,\n version_banco: versionBanco,\n version_coincide: Boolean(versionCoincide),\n version_contradice: Boolean(versionContradice),\n\n anio_propuesta: anioPropuesta,\n anio_banco: anioBanco,\n anio_coincide: Boolean(anioCoincide),\n anio_contradice: Boolean(anioContradice),\n\n pais_coincide: Boolean(paisCoincide),\n marca_coincide: Boolean(marcaCoincide),\n\n regla_match: reglaMatch,\n\n banco_row_number: bancoRowNumber,\n banco_nombre: bancoNombre,\n banco_tipo_accion: bancoTipoAccion,\n banco_marca: bancoMarca,\n banco_cliente: bancoCliente,\n banco_pais: bancoPais,\n banco_canal: bancoCanal,\n banco_link: bancoLink,\n banco_file_id: bancoFileId,\n banco_enlaces_ejecutadas: bancoEnlacesEjecutadas\n });\n}\n\nconst candidatosValidos = candidatos\n .filter(c => c.score > 0)\n .sort((a, b) => {\n if (b.score_orden !== a.score_orden) return b.score_orden - a.score_orden;\n return Number(b.banco_row_number || 0) - Number(a.banco_row_number || 0);\n });\n\nconst mejor = candidatosValidos[0] || null;\nconst segundo = candidatosValidos[1] || null;\n\nlet propuestaMatchEstado = 'SIN_MATCH';\nlet propuestaMatchRevision = 'REQUIERE_REVISION_MANUAL';\nlet propuestaMatchConfianza = '0';\nlet propuestaNombreBanco = '';\nlet propuestaLinkBanco = '';\nlet propuestaBancoRowNumber = '';\nlet propuestaBancoEnlacesExistentes = '';\nlet propuestaBancoFileId = '';\n\nif (mejor) {\n let confianzaFinal = mejor.score_porcentaje;\n\n // Si el segundo está demasiado cerca, mantenemos match pero pedimos revisión.\n const segundoMuyCerca =\n segundo &&\n mejor.score_porcentaje >= 85 &&\n segundo.score_porcentaje >= 85 &&\n Math.abs(mejor.score_porcentaje - segundo.score_porcentaje) <= 2 &&\n normalizar(mejor.banco_nombre) !== normalizar(segundo.banco_nombre);\n\n propuestaNombreBanco = mejor.banco_nombre || 'No identificado';\n propuestaLinkBanco = mejor.banco_link || '';\n propuestaBancoRowNumber = mejor.banco_row_number || '';\n propuestaBancoEnlacesExistentes = mejor.banco_enlaces_ejecutadas || '';\n propuestaBancoFileId = mejor.banco_file_id || '';\n\n if (confianzaFinal >= 85 && !segundoMuyCerca) {\n propuestaMatchEstado = 'MATCH_ALTA_CONFIANZA';\n propuestaMatchRevision = 'NO_REQUIERE_REVISION';\n } else if (confianzaFinal >= 85 && segundoMuyCerca) {\n propuestaMatchEstado = 'MATCH_MEDIA_CONFIANZA';\n propuestaMatchRevision = 'REQUIERE_VALIDACION_MANUAL';\n confianzaFinal = Math.min(confianzaFinal, 84);\n } else if (confianzaFinal >= 60) {\n propuestaMatchEstado = 'MATCH_MEDIA_CONFIANZA';\n propuestaMatchRevision = 'REQUIERE_VALIDACION_MANUAL';\n } else {\n propuestaMatchEstado = 'MATCH_BAJA_CONFIANZA';\n propuestaMatchRevision = 'REQUIERE_REVISION_MANUAL';\n }\n\n propuestaMatchConfianza = String(confianzaFinal);\n}\n\nreturn [\n {\n json: {\n ...propuesta,\n\n propuesta_match_estado: propuestaMatchEstado,\n propuesta_match_confianza: propuestaMatchConfianza,\n propuesta_nombre_banco: propuestaNombreBanco,\n propuesta_link_banco: propuestaLinkBanco,\n propuesta_match_revision: propuestaMatchRevision,\n\n propuesta_banco_row_number: propuestaBancoRowNumber,\n propuesta_banco_enlaces_existentes: propuestaBancoEnlacesExistentes,\n propuesta_banco_file_id: propuestaBancoFileId,\n\n match_banco_debug: {\n total_filas_banco: bancoRows.length,\n total_candidatos: candidatosValidos.length,\n propuesta_normalizada: {\n referencia,\n marca,\n cliente,\n pais,\n version_propuesta: versionPropuesta,\n anio_propuesta: anioPropuesta,\n texto_clave: textoClavePropuesta\n },\n mejor_match: mejor\n ? {\n score: mejor.score_porcentaje,\n score_orden: Math.round(mejor.score_orden * 100),\n regla_match: mejor.regla_match,\n row_number: mejor.banco_row_number,\n nombre: mejor.banco_nombre,\n tipo_accion: mejor.banco_tipo_accion,\n marca: mejor.banco_marca,\n cliente: mejor.banco_cliente,\n pais: mejor.banco_pais,\n canal: mejor.banco_canal,\n link: mejor.banco_link,\n file_id: mejor.banco_file_id,\n enlaces_ejecutadas_existentes: mejor.banco_enlaces_ejecutadas,\n score_nombre: mejor.score_nombre,\n score_referencia_nombre: mejor.score_referencia_nombre,\n score_evidencia_nombre: mejor.score_evidencia_nombre,\n cobertura_nombre_en_evidencia: mejor.cobertura_nombre_en_evidencia,\n cobertura_referencia_en_banco: mejor.cobertura_referencia_en_banco,\n score_marca: mejor.score_marca,\n score_cliente: mejor.score_cliente,\n score_pais: mejor.score_pais,\n score_general: mejor.score_general,\n score_clave: mejor.score_clave,\n cobertura_banco_en_propuesta: mejor.cobertura_banco_en_propuesta,\n comunes_nombre_evidencia: mejor.comunes_nombre_evidencia,\n version_banco: mejor.version_banco,\n version_coincide: mejor.version_coincide,\n anio_banco: mejor.anio_banco,\n anio_coincide: mejor.anio_coincide,\n pais_coincide: mejor.pais_coincide,\n marca_coincide: mejor.marca_coincide\n }\n : null,\n segundo_match: segundo\n ? {\n score: segundo.score_porcentaje,\n regla_match: segundo.regla_match,\n row_number: segundo.banco_row_number,\n nombre: segundo.banco_nombre,\n marca: segundo.banco_marca,\n cliente: segundo.banco_cliente,\n pais: segundo.banco_pais,\n file_id: segundo.banco_file_id\n }\n : null,\n top_5: candidatosValidos.slice(0, 5).map(c => ({\n score: c.score_porcentaje,\n score_orden: Math.round(c.score_orden * 100),\n regla_match: c.regla_match,\n row_number: c.banco_row_number,\n nombre: c.banco_nombre,\n marca: c.banco_marca,\n cliente: c.banco_cliente,\n pais: c.banco_pais,\n file_id: c.banco_file_id,\n score_nombre: c.score_nombre,\n score_clave: c.score_clave,\n cobertura_banco_en_propuesta: c.cobertura_banco_en_propuesta,\n comunes_nombre_evidencia: c.comunes_nombre_evidencia,\n version_banco: c.version_banco,\n version_coincide: c.version_coincide,\n anio_banco: c.anio_banco,\n anio_coincide: c.anio_coincide\n }))\n }\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 0, 1360 ], "id": "2043ba80-2789-4cc7-a261-943c464dd2d6", "name": "Code - Match propuesta banco Fulgencio TEST" }, { "parameters": { "jsCode": "const actual = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst jsonFinal = getNodeJson('Code - Normalizar JSON final propuesta ejecutada TEST');\nconst match = getNodeJson('Code - Match propuesta banco Fulgencio TEST');\n\nconst slidesNormalizado1 = getNodeJson('Code - Normalizar respuesta Slides TEST');\nconst slidesNormalizado2 = getNodeJson('Code - Normalizar respuesta Slides ejecución TEST');\nconst slidesNormalizado3 = getNodeJson('Code - Normalizar link presentación ejecución TEST');\n\nconst data = {\n ...jsonFinal,\n ...actual,\n ...slidesNormalizado1,\n ...slidesNormalizado2,\n ...slidesNormalizado3,\n ...match\n};\n\nconst matchDebug =\n match.match_banco_debug ||\n actual.match_banco_debug ||\n data.match_banco_debug ||\n {};\n\nconst mejorMatch = matchDebug.mejor_match || {};\n\nconst tipoReporte = limpiar(\n match.tipo_reporte ||\n data.tipo_reporte ||\n actual.tipo_reporte ||\n ''\n).toUpperCase();\n\nlet matchEstado = limpiar(\n match.propuesta_match_estado ||\n data.propuesta_match_estado ||\n actual.propuesta_match_estado ||\n ''\n).toUpperCase();\n\nlet matchConfianza = Number(\n match.propuesta_match_confianza ||\n data.propuesta_match_confianza ||\n actual.propuesta_match_confianza ||\n mejorMatch.score ||\n 0\n);\n\nlet nombreBanco = limpiar(\n match.propuesta_nombre_banco ||\n data.propuesta_nombre_banco ||\n actual.propuesta_nombre_banco ||\n mejorMatch.nombre ||\n ''\n);\n\nconst linkBancoOriginal = limpiar(\n match.propuesta_link_banco ||\n data.propuesta_link_banco ||\n actual.propuesta_link_banco ||\n mejorMatch.link ||\n ''\n);\n\nconst fileIdBanco = limpiar(\n match.propuesta_banco_file_id ||\n data.propuesta_banco_file_id ||\n actual.propuesta_banco_file_id ||\n mejorMatch.file_id ||\n ''\n);\n\nconst rowNumberBanco = limpiar(\n match.propuesta_banco_row_number ||\n data.propuesta_banco_row_number ||\n actual.propuesta_banco_row_number ||\n mejorMatch.row_number ||\n ''\n);\n\nconst enlacesExistentes = limpiar(\n match.propuesta_banco_enlaces_existentes ||\n data.propuesta_banco_enlaces_existentes ||\n actual.propuesta_banco_enlaces_existentes ||\n data.banco_enlaces_existentes ||\n actual.banco_enlaces_existentes ||\n mejorMatch.enlaces_ejecutadas_existentes ||\n ''\n);\n\nlet presentationUrl = limpiar(\n data.presentacion_ejecucion_url ||\n actual.presentacion_ejecucion_url ||\n data.presentation_url ||\n actual.presentation_url ||\n data.presentationUrl ||\n actual.presentationUrl ||\n data.webViewLink ||\n actual.webViewLink ||\n data.link_presentacion ||\n actual.link_presentacion ||\n ''\n);\n\nconst presentationId = limpiar(\n data.presentation_id ||\n actual.presentation_id ||\n ''\n);\n\nif (!presentationUrl && presentationId) {\n presentationUrl = `https://docs.google.com/presentation/d/${presentationId}/edit`;\n}\n\n// Fallback defensivo por si el estado quedó viejo.\nif (\n matchEstado !== 'MATCH_ALTA_CONFIANZA' &&\n matchConfianza >= 85\n) {\n matchEstado = 'MATCH_ALTA_CONFIANZA';\n}\n\nif (\n matchEstado !== 'MATCH_MEDIA_CONFIANZA' &&\n matchEstado !== 'MATCH_ALTA_CONFIANZA' &&\n matchConfianza >= 60 &&\n matchConfianza < 85\n) {\n matchEstado = 'MATCH_MEDIA_CONFIANZA';\n}\n\nif (!matchEstado || matchEstado === 'PENDIENTE') {\n if (matchConfianza >= 85) {\n matchEstado = 'MATCH_ALTA_CONFIANZA';\n } else if (matchConfianza >= 60) {\n matchEstado = 'MATCH_MEDIA_CONFIANZA';\n } else if (matchConfianza > 0) {\n matchEstado = 'MATCH_BAJA_CONFIANZA';\n }\n}\n\n// Formato limpio para el banco original:\n// Solo links, uno por línea.\nlet enlacesActualizados = enlacesExistentes;\n\nif (presentationUrl && !enlacesExistentes.includes(presentationUrl)) {\n enlacesActualizados = enlacesExistentes\n ? `${enlacesExistentes}\\n${presentationUrl}`\n : presentationUrl;\n}\n\nconst motivos = [];\n\nif (tipoReporte !== 'PROPUESTA_EJECUTADA') {\n motivos.push(`tipo_reporte es ${tipoReporte || 'NO_DETERMINADO'}`);\n}\n\nif (matchEstado !== 'MATCH_ALTA_CONFIANZA') {\n motivos.push(`propuesta_match_estado es ${matchEstado || 'SIN_MATCH'}`);\n}\n\nif (matchConfianza < 85) {\n motivos.push(`propuesta_match_confianza es ${matchConfianza}`);\n}\n\nif (!presentationUrl) {\n motivos.push('no existe presentacion_ejecucion_url');\n}\n\nif (!nombreBanco) {\n motivos.push('no llegó propuesta_nombre_banco');\n}\n\nif (!rowNumberBanco) {\n motivos.push('no llegó propuesta_banco_row_number');\n}\n\nif (presentationUrl && enlacesExistentes.includes(presentationUrl)) {\n motivos.push('el link ya existe en la columna de enlaces ejecutados');\n}\n\nconst actualizarBanco = motivos.length === 0;\n\nconst motivoNoActualizaBanco = actualizarBanco\n ? ''\n : `No se actualiza banco porque ${motivos.join('; ')}.`;\n\nreturn [\n {\n json: {\n ...data,\n\n actualizar_banco_original: actualizarBanco,\n motivo_no_actualiza_banco: motivoNoActualizaBanco,\n\n propuesta_match_estado: matchEstado || data.propuesta_match_estado || 'SIN_MATCH',\n propuesta_match_confianza: String(matchConfianza || 0),\n propuesta_nombre_banco: nombreBanco,\n propuesta_link_banco: linkBancoOriginal,\n propuesta_banco_file_id: fileIdBanco,\n propuesta_banco_row_number: rowNumberBanco,\n propuesta_banco_enlaces_existentes: enlacesExistentes,\n\n banco_match_column: 'row_number',\n banco_match_value: rowNumberBanco,\n\n row_number: rowNumberBanco,\n 'Enlaces a propuestas ejecutadas': enlacesActualizados,\n\n banco_enlaces_existentes: enlacesExistentes,\n banco_enlaces_actualizados: enlacesActualizados,\n banco_presentacion_url: presentationUrl,\n\n banco_update_debug: {\n tipo_reporte: tipoReporte,\n propuesta_match_estado: matchEstado,\n propuesta_match_confianza: matchConfianza,\n propuesta_nombre_banco: nombreBanco,\n propuesta_link_banco: linkBancoOriginal,\n propuesta_banco_file_id: fileIdBanco,\n propuesta_banco_row_number: rowNumberBanco,\n presentationUrl,\n enlacesExistentes,\n enlacesActualizados,\n actualizarBanco,\n motivoNoActualizaBanco,\n mejor_match: mejorMatch,\n match_debug: matchDebug\n }\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 6928, 1232 ], "id": "3dd8a8af-4902-4a0f-92b5-94e921262ae4", "name": "Code - Preparar actualización banco propuesta ejecutada TEST" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "dccbea1d-e9cf-4f51-b94d-7b0162737548", "leftValue": "={{ $json.actualizar_banco_original === true }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ 7696, 1280 ], "id": "92a6348b-03df-4edc-b1e7-994621c331b7", "name": "IF - Actualizar banco match alta confianza TEST" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1iy3lC3DS3ZfShb7d26iQOrPjx9s_3B-PC07XfYew7ng", "mode": "list", "cachedResultName": "BANCO DE PROPUESTAS DE CDC PARA FULGENCIO FUMADO", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iy3lC3DS3ZfShb7d26iQOrPjx9s_3B-PC07XfYew7ng/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": "gid=0", "mode": "list", "cachedResultName": "propuestas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iy3lC3DS3ZfShb7d26iQOrPjx9s_3B-PC07XfYew7ng/edit#gid=0" }, "columns": { "mappingMode": "defineBelow", "value": { "Enlaces a propuestas ejecutadas": "={{ $json.banco_enlaces_actualizados }}", "row_number": "={{ $json.propuesta_banco_row_number }}" }, "matchingColumns": [ "row_number" ], "schema": [ { "id": "NOMBRE", "displayName": "NOMBRE", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "TIPO DE ACCION", "displayName": "TIPO DE ACCION", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "CLIENTE", "displayName": "CLIENTE", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "MARCA", "displayName": "MARCA", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "PAIS", "displayName": "PAIS", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "CANAL", "displayName": "CANAL", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "AMBIENTE DE COMPRA (RE)", "displayName": "AMBIENTE DE COMPRA (RE)", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "TÁCTICA PROMOCIONAL", "displayName": "TÁCTICA PROMOCIONAL", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "APROBADA", "displayName": "APROBADA", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "ETIQUETAS", "displayName": "ETIQUETAS", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "AÑO", "displayName": "AÑO", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "Enlace a la propuesta", "displayName": "Enlace a la propuesta", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "Descripcion", "displayName": "Descripcion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "file_id", "displayName": "file_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "nombre_archivo", "displayName": "nombre_archivo", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "mime_type", "displayName": "mime_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "fuente_pais", "displayName": "fuente_pais", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "confianza_pais", "displayName": "confianza_pais", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "requiere_revision", "displayName": "requiere_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "procesado_ia", "displayName": "procesado_ia", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "ultima_actualizacion", "displayName": "ultima_actualizacion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "motivos_revision", "displayName": "motivos_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "Enlaces a propuestas ejecutadas", "displayName": "Enlaces a propuestas ejecutadas", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "tablero_sync_key", "displayName": "tablero_sync_key", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "tablero_project_id", "displayName": "tablero_project_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "tablero_origen", "displayName": "tablero_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "drive_modified_time", "displayName": "drive_modified_time", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "ultimo_refresco_ia", "displayName": "ultimo_refresco_ia", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "refresh_status", "displayName": "refresh_status", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "refresh_attempts", "displayName": "refresh_attempts", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "converted_google_slides_id", "displayName": "converted_google_slides_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "converted_google_slides_link", "displayName": "converted_google_slides_link", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": false } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ 8208, 1072 ], "id": "7c0dc504-1094-461d-82d6-469db24200b9", "name": "Sheets - Actualizar link ejecutada en banco TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const respuestaBanco = $json || {};\n\nconst base = $('Code - Preparar actualización banco propuesta ejecutada TEST').first().json || {};\n\nreturn [\n {\n json: {\n ...base,\n\n banco_original_actualizado: true,\n banco_original_response: respuestaBanco\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 8416, 1072 ], "id": "f9965d96-e94f-4a44-8604-b098fe9a08b9", "name": "Code - Restaurar contexto actualización banco TEST" } ], "connections": { "Code - Normalizar evento WhatsApp TEST": { "main": [ [ { "node": "Mi numero TESTING", "type": "main", "index": 0 } ] ] }, "Mi numero TESTING": { "main": [ [ { "node": "Sheets - Leer sesiones existentes", "type": "main", "index": 0 } ] ] }, "Webhook - Evolution WhatsApp TEST": { "main": [ [ { "node": "Code - Normalizar evento WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "WhatsApp - Enviar mensaje TEST": { "main": [ [] ] }, "Sheets - Leer sesiones existentes": { "main": [ [ { "node": "Code - Resolver sesión WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Resolver sesión WhatsApp TEST": { "main": [ [ { "node": "Switch - Decisión sesión WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Switch - Decisión sesión WhatsApp TEST": { "main": [ [ { "node": "Sheets - Crear sesión WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar aviso sesión activa WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar sin sesión para cancelar WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar cancelación sesión WhatsApp TEST", "type": "main", "index": 0 } ], [], [ { "node": "Code - Resolver paso activo WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar bienvenida WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Crear sesión WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar bienvenida WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Resolver paso activo WhatsApp TEST": { "main": [ [ { "node": "Switch - Paso activo WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Switch - Paso activo WhatsApp TEST": { "main": [ [ { "node": "Sheets - Guardar evento audio WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar solicitud audio WhatsApp TEST", "type": "main", "index": 0 } ], [], [], [ { "node": "Sheets - Guardar evento imagen WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Sheets - Leer eventos sesión fotos WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar aviso falta imagen WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Sheets - Guardar evento video WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "CERRAR_SIN_VIDEO", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar cierre con videos WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar recordatorio videos WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar aviso procesando WhatsApp TEST", "type": "main", "index": 0 } ], [], [] ] }, "Code - Preparar solicitud audio WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Guardar evento audio WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar actualización audio recibido WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar actualización audio recibido WhatsApp TEST": { "main": [ [ { "node": "Sheets - Actualizar sesión audio recibido WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar sesión audio recibido WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar solicitud imágenes WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar aviso sesión activa WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar solicitud imágenes WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar cancelación sesión WhatsApp TEST": { "main": [ [ { "node": "Sheets - Cancelar sesión WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Cancelar sesión WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar mensaje cancelación WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar mensaje cancelación WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Code — Preparar actualización imagen recibida": { "main": [ [ { "node": "Sheets — Actualizar sesión imagen recibida", "type": "main", "index": 0 } ] ] }, "Sheets — Actualizar sesión imagen recibida": { "main": [ [ { "node": "Code — Preparar confirmación imagen", "type": "main", "index": 0 } ] ] }, "Sheets - Guardar evento imagen WhatsApp TEST": { "main": [ [ { "node": "Code — Preparar actualización imagen recibida", "type": "main", "index": 0 } ] ] }, "Code - Preparar aviso falta imagen WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Code — Preparar confirmación imagen": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Preparar actualización fotos listas": { "main": [ [ { "node": "Sheets - Actualizar sesión fotos listas WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar sesión fotos listas WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar solicitud videos WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar solicitud videos WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar recordatorio videos WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "CERRAR_SIN_VIDEO": { "main": [ [ { "node": "Sheets - Actualizar sesión cierre sin video WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar sesión cierre sin video WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar aviso procesando WhatsApp TEST1", "type": "main", "index": 0 } ] ] }, "Sheets - Guardar evento video WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar actualización video recibido WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar actualización video recibido WhatsApp TEST": { "main": [ [ { "node": "Sheets - Actualizar sesión video recibido WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar sesión video recibido WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar confirmación video WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar confirmación video WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar cierre con videos WhatsApp TEST": { "main": [ [ { "node": "Sheets - Actualizar sesión cierre con videos WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar sesión cierre con videos WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar aviso procesando WhatsApp TEST1", "type": "main", "index": 0 } ] ] }, "Code - Preparar aviso procesando WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar búsqueda eventos media WhatsApp TEST": { "main": [ [ { "node": "Sheets - Leer eventos WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Leer eventos WhatsApp TEST": { "main": [ [ { "node": "Code - Filtrar eventos media de sesión WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Filtrar eventos media de sesión WhatsApp TEST": { "main": [ [ { "node": "Code - Separar media en items WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Separar media en items WhatsApp TEST": { "main": [ [ { "node": "HTTP Request - Obtener media base64 Evolution TEST", "type": "main", "index": 0 } ] ] }, "HTTP Request - Obtener media base64 Evolution TEST": { "main": [ [ { "node": "Code - Convertir base64 a binario WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Convertir base64 a binario WhatsApp TEST": { "main": [ [ { "node": "Drive - Subir media WhatsApp TEST", "type": "main", "index": 0 }, { "node": "Switch - Tipo media para Gemini WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Drive - Subir media WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar registro media Drive WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar registro media Drive WhatsApp TEST": { "main": [ [ { "node": "Sheets - Guardar media Drive WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Guardar media Drive WhatsApp TEST": { "main": [ [ { "node": "Code - Consolidar media recuperada WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Consolidar media recuperada WhatsApp TEST": { "main": [ [ { "node": "Sheets - Actualizar sesión media recuperada WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar sesión media recuperada WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar paquete análisis Gemini WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Switch - Tipo media para Gemini WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar Gemini audio WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar Gemini imagen WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar Gemini video WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar Gemini audio WhatsApp TEST": { "main": [ [ { "node": "Gemini - Analizar audio WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Gemini - Analizar audio WhatsApp TEST": { "main": [ [ { "node": "Code - Normalizar análisis audio WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar Gemini imagen WhatsApp TEST": { "main": [ [ { "node": "Gemini - Analizar imagen WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Gemini - Analizar imagen WhatsApp TEST": { "main": [ [ { "node": "Code - Normalizar análisis imagen WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar Gemini video WhatsApp TEST": { "main": [ [ { "node": "Analyze video", "type": "main", "index": 0 } ] ] }, "Analyze video": { "main": [ [ { "node": "Code - Normalizar análisis video WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Normalizar análisis video WhatsApp TEST": { "main": [ [ { "node": "Sheets - Guardar análisis media Gemini WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Normalizar análisis imagen WhatsApp TEST": { "main": [ [ { "node": "Sheets - Guardar análisis media Gemini WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Normalizar análisis audio WhatsApp TEST": { "main": [ [ { "node": "Sheets - Guardar análisis media Gemini WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Guardar análisis media Gemini WhatsApp TEST": { "main": [ [ { "node": "IF - Es último análisis media WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Leer análisis media Gemini WhatsApp TEST": { "main": [ [ { "node": "Code - Consolidar análisis media Gemini WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Consolidar análisis media Gemini WhatsApp TEST": { "main": [ [ { "node": "Gemini - Generar JSON final propuesta ejecutada TEST", "type": "main", "index": 0 } ] ] }, "Gemini - Generar JSON final propuesta ejecutada TEST": { "main": [ [ { "node": "Code - Normalizar JSON final propuesta ejecutada TEST", "type": "main", "index": 0 } ] ] }, "Code - Normalizar JSON final propuesta ejecutada TEST": { "main": [ [ { "node": "Sheets - Leer banco propuestas Fulgencio TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Guardar propuesta ejecutada final TEST": { "main": [ [ { "node": "Code - Preparar copia presentación ejecución TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar sesión análisis completado WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar mensaje final WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar mensaje final WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "IF - Es último análisis media WhatsApp TEST": { "main": [ [ { "node": "Wait", "type": "main", "index": 0 } ] ] }, "Wait": { "main": [ [ { "node": "Sheets - Leer análisis media Gemini WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Leer propuestas ejecutadas final TEST": { "main": [ [ { "node": "Code - Verificar duplicado propuesta ejecutada TEST", "type": "main", "index": 0 } ] ] }, "Code - Verificar duplicado propuesta ejecutada TEST": { "main": [ [ { "node": "IF - Propuesta final ya existe TEST", "type": "main", "index": 0 } ] ] }, "IF - Propuesta final ya existe TEST": { "main": [ [], [ { "node": "Sheets - Guardar propuesta ejecutada final TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar copia presentación ejecución TEST": { "main": [ [ { "node": "Code - Preparar carpeta ejecución TEST", "type": "main", "index": 0 } ] ] }, "Drive - Copiar plantilla presentación ejecución TEST": { "main": [ [ { "node": "Code - Normalizar link presentación ejecución TEST", "type": "main", "index": 0 } ] ] }, "Code - Normalizar link presentación ejecución TEST": { "main": [ [ { "node": "Code - Preparar reemplazos Slides ejecución TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar link presentación ejecución TEST": { "main": [ [ { "node": "Code - Preparar actualización banco propuesta ejecutada TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar reemplazos Slides ejecución TEST": { "main": [ [ { "node": "HTTP Request - Reemplazar textos Slides ejecución TEST", "type": "main", "index": 0 } ] ] }, "HTTP Request - Reemplazar textos Slides ejecución TEST": { "main": [ [ { "node": "Code - Preparar payload insertar imágenes Slides TEST", "type": "main", "index": 0 } ] ] }, "Code - Normalizar respuesta Slides ejecución TEST": { "main": [ [ { "node": "Sheets - Actualizar link presentación ejecución TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar sin sesión para cancelar WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Leer eventos sesión fotos WhatsApp TEST": { "main": [ [ { "node": "Preparar actualización fotos listas", "type": "main", "index": 0 } ] ] }, "Code - Preparar carpeta ejecución TEST": { "main": [ [ { "node": "Drive - Crear carpeta ejecución TEST", "type": "main", "index": 0 } ] ] }, "Drive - Crear carpeta ejecución TEST": { "main": [ [ { "node": "Code - Normalizar carpeta ejecución TEST", "type": "main", "index": 0 } ] ] }, "Code - Normalizar carpeta ejecución TEST": { "main": [ [ { "node": "Code - Preparar media para mover carpeta TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar media para mover carpeta TEST": { "main": [ [ { "node": "Drive - Mover media a carpeta ejecución TEST", "type": "main", "index": 0 } ] ] }, "Drive - Mover media a carpeta ejecución TEST": { "main": [ [ { "node": "Code - Confirmar media movida carpeta TEST", "type": "main", "index": 0 } ] ] }, "Code - Confirmar media movida carpeta TEST": { "main": [ [ { "node": "Drive - Copiar plantilla presentación ejecución TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar payload insertar imágenes Slides TEST": { "main": [ [ { "node": "HTTP - Insertar imágenes en Slides TEST", "type": "main", "index": 0 } ] ] }, "HTTP - Insertar imágenes en Slides TEST": { "main": [ [ { "node": "Code - Normalizar respuesta Slides ejecución TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar aviso procesando WhatsApp TEST1": { "main": [ [ { "node": "HTTP - Enviar aviso procesando WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Restaurar contexto aviso procesando TEST": { "main": [ [ { "node": "Code - Preparar búsqueda eventos media WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "HTTP - Enviar aviso procesando WhatsApp TEST": { "main": [ [ { "node": "Code - Restaurar contexto aviso procesando TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Leer banco propuestas Fulgencio TEST": { "main": [ [ { "node": "Code - Match propuesta banco Fulgencio TEST", "type": "main", "index": 0 } ] ] }, "Code - Match propuesta banco Fulgencio TEST": { "main": [ [ { "node": "Sheets - Leer propuestas ejecutadas final TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar actualización banco propuesta ejecutada TEST": { "main": [ [ { "node": "IF - Actualizar banco match alta confianza TEST", "type": "main", "index": 0 } ] ] }, "IF - Actualizar banco match alta confianza TEST": { "main": [ [ { "node": "Sheets - Actualizar link ejecutada en banco TEST", "type": "main", "index": 0 } ], [ { "node": "Sheets - Actualizar sesión análisis completado WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar link ejecutada en banco TEST": { "main": [ [ { "node": "Code - Restaurar contexto actualización banco TEST", "type": "main", "index": 0 } ] ] }, "Code - Restaurar contexto actualización banco TEST": { "main": [ [ { "node": "Sheets - Actualizar sesión análisis completado WhatsApp TEST", "type": "main", "index": 0 } ] ] } }, "settings": { "executionOrder": "v1", "binaryMode": "separate" }, "staticData": null, "meta": { "templateCredsSetupCompleted": true }, "versionId": "94d48ce3-27cf-4be2-8d30-467fb212e28b", "activeVersionId": "94d48ce3-27cf-4be2-8d30-467fb212e28b", "versionCounter": 1727, "triggerCount": 1, "shared": [ { "updatedAt": "2026-06-08T20:09:28.138Z", "createdAt": "2026-06-08T20:09:28.138Z", "role": "workflow:owner", "workflowId": "sXH75CXIQ0sEzm1Y", "projectId": "PJpTANzTXIFibWsW", "project": { "updatedAt": "2026-04-22T14:25:09.686Z", "createdAt": "2026-04-22T14:22:54.790Z", "id": "PJpTANzTXIFibWsW", "name": "Isaac Aracena ", "type": "personal", "icon": null, "description": null, "creatorId": "0a88c0b1-928e-4412-896e-c5d1c99b2029" } } ], "tags": [], "activeVersion": { "updatedAt": "2026-06-20T16:00:09.000Z", "createdAt": "2026-06-20T15:59:44.174Z", "versionId": "94d48ce3-27cf-4be2-8d30-467fb212e28b", "workflowId": "sXH75CXIQ0sEzm1Y", "nodes": [ { "parameters": { "method": "POST", "url": "={{'https://wsp.gomezleemarketing.com'}}/message/sendText/{{'botsoporte'}}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "apikey", "value": "={{'C267126ABB45-4C12-B626-6BAB1833F5D7'}}" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ { "name": "number", "value": "={{ (() => {\n const limpiar = (valor) => String(valor ?? '').trim();\n\n const candidatos = [\n $json.whatsapp_to,\n $json.group_jid,\n $json.whatsapp_remote_jid,\n $json.sesion_activa?.group_jid,\n $json.sesion_activa?.whatsapp_to,\n $json.sesion_activa?.whatsapp_remote_jid,\n $json.manager_telefono,\n $json.sender_phone\n ];\n\n let raw = limpiar(candidatos.find(v => limpiar(v)));\n\n if (!raw) return '';\n\n raw = raw.replace('@c.us', '@s.whatsapp.net');\n\n // Si ya viene como grupo, conservarlo.\n if (raw.includes('@g.us')) {\n return raw;\n }\n\n // Si viene como usuario WhatsApp, validar si realmente era un grupo.\n if (raw.includes('@s.whatsapp.net')) {\n const numero = raw.replace('@s.whatsapp.net', '').replace(/\\D/g, '');\n\n // Los grupos de WhatsApp suelen venir como 120363...\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n }\n\n const numero = raw.replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n})() }}" }, { "name": "delay", "value": "={{ 1000 }}" }, { "name": "text", "value": "={{ $json.whatsapp_text || $json.telegram_text || $json.texto_respuesta || $json.mensaje || $json.text || 'Mensaje sin contenido' }}" } ] }, "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ 9984, 704 ], "id": "b1d4c2c3-c77f-4b9d-b4ca-c40535c26299", "name": "WhatsApp - Enviar mensaje TEST" }, { "parameters": { "jsCode": "const input = $json || {};\nconst body = input.body || input;\nconst data = body.data || {};\n\nconst texto = (valor) => String(valor ?? '').trim();\n\nconst limpiarNumero = (valor) =>\n texto(valor)\n .replace('@s.whatsapp.net', '')\n .replace('@c.us', '')\n .replace('@g.us', '')\n .replace('@lid', '')\n .replace(/\\D/g, '');\n\nconst key = data.key || {};\nconst message = data.message || {};\n\n// --------------------------------------------------\n// 1. Identificar origen real del mensaje\n// --------------------------------------------------\n\nconst remoteJid = texto(\n key.remoteJid ||\n data.remoteJid ||\n body.remoteJid ||\n ''\n);\n\nconst isGroup = remoteJid.endsWith('@g.us');\n\nconst participantRaw = texto(\n key.participant ||\n data.participant ||\n body.participant ||\n data.sender ||\n data.participantJid ||\n ''\n);\n\nconst participantAlt = texto(\n key.participantAlt ||\n data.participantAlt ||\n body.participantAlt ||\n data.senderAlt ||\n ''\n);\n\n// En grupos, Evolution puede mandar:\n// participant = 1655...@lid\n// participantAlt = número real@s.whatsapp.net\n// Para sesiones y permisos necesitamos preferir el número real.\nconst participantJid = participantAlt || participantRaw;\n\n// En grupo, el sender real es participantAlt si existe.\n// En chat individual, el sender real es remoteJid.\nconst senderJid = isGroup\n ? participantJid\n : remoteJid;\n\nconst senderPhone = limpiarNumero(senderJid);\nconst groupJid = isGroup ? remoteJid : '';\n\nconst pushName =\n texto(data.pushName) ||\n texto(body.pushName) ||\n texto(data.senderName) ||\n texto(body.senderName) ||\n 'Usuario WhatsApp';\n\n// El bot debe responder al grupo si el mensaje vino de grupo.\n// Si vino de chat individual, responde al usuario.\nconst whatsappTo = isGroup ? groupJid : remoteJid;\n\n// --------------------------------------------------\n// 2. Extraer texto o payload de botones\n// --------------------------------------------------\n\nconst buttonText =\n texto(message.buttonsResponseMessage?.selectedButtonId) ||\n texto(message.buttonsResponseMessage?.selectedDisplayText) ||\n texto(message.listResponseMessage?.singleSelectReply?.selectedRowId) ||\n texto(message.listResponseMessage?.title) ||\n texto(message.templateButtonReplyMessage?.selectedId) ||\n texto(message.templateButtonReplyMessage?.selectedDisplayText) ||\n '';\n\nconst textoMensaje =\n buttonText ||\n texto(message.conversation) ||\n texto(message.extendedTextMessage?.text) ||\n texto(data.messageText) ||\n texto(data.text) ||\n texto(body.text) ||\n '';\n\n// --------------------------------------------------\n// 3. Detectar media\n// --------------------------------------------------\n\nconst audioMsg = message.audioMessage;\nconst imageMsg = message.imageMessage;\nconst videoMsg = message.videoMessage;\nconst documentMsg = message.documentMessage;\n\nconst documentMime = texto(documentMsg?.mimetype);\nconst documentFileName = texto(documentMsg?.fileName);\n\nlet messageType = 'unknown';\n\nif (audioMsg) {\n messageType = 'audio';\n} else if (imageMsg) {\n messageType = 'image';\n} else if (videoMsg) {\n messageType = 'video';\n} else if (documentMsg && documentMime.startsWith('video/')) {\n messageType = 'video';\n} else if (documentMsg && documentMime.startsWith('image/')) {\n messageType = 'image';\n} else if (documentMsg && documentMime.startsWith('audio/')) {\n messageType = 'audio';\n} else if (documentMsg) {\n messageType = 'document';\n} else if (textoMensaje) {\n messageType = 'text';\n}\n\nconst tieneMedia = ['audio', 'image', 'video', 'document'].includes(messageType);\n\nconst mediaMimeType =\n texto(audioMsg?.mimetype) ||\n texto(imageMsg?.mimetype) ||\n texto(videoMsg?.mimetype) ||\n documentMime ||\n '';\n\nconst mediaSourceId =\n texto(key.id) ||\n texto(data.id) ||\n texto(data.messageId) ||\n '';\n\n// --------------------------------------------------\n// 4. Normalizar comandos internos\n// --------------------------------------------------\n\nconst normalizarTexto = (valor) =>\n texto(valor)\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toUpperCase();\n\nconst normalizarComando = (valor) => {\n const t = normalizarTexto(valor);\n\n // Nuevo inicio oficial\n if (t === 'HEY') return 'START';\n\n // Compatibilidad temporal\n if (\n t === '/START' ||\n t === 'START' ||\n t === 'INICIAR'\n) {\n return 'START';\n}\n\n if (t === 'CANCELAR' || t === 'CANCEL') {\n return 'CANCELAR';\n }\n\n if (\n t === 'FOTOS LISTAS' ||\n t === 'FOTOS_LISTAS' ||\n t === 'IMAGENES LISTAS' ||\n t === 'IMAGENES_LISTAS' ||\n t === 'PHOTOS READY' ||\n t === 'PHOTOS_READY'\n ) {\n return 'FOTOS_LISTAS';\n }\n\n if (\n t === 'SIN VIDEO' ||\n t === 'SIN_VIDEO' ||\n t === 'NO VIDEO' ||\n t === 'NO_VIDEO'\n ) {\n return 'SIN_VIDEO';\n }\n\n if (\n t === 'LISTO' ||\n t === 'DONE' ||\n t === 'FINISH' ||\n t === 'FINALIZAR'\n ) {\n return 'LISTO';\n }\n\n return 'NORMAL';\n};\n\n// --------------------------------------------------\n// 5. Detectar país/idioma base por teléfono\n// --------------------------------------------------\n\nconst detectarIdiomaPorTelefono = (phone) => {\n const n = limpiarNumero(phone);\n\n // Países GLM principalmente hispanohablantes\n if (n.startsWith('502')) return { country_code: '502', pais_detectado: 'Guatemala', idioma_flujo: 'ES' };\n if (n.startsWith('503')) return { country_code: '503', pais_detectado: 'El Salvador', idioma_flujo: 'ES' };\n if (n.startsWith('504')) return { country_code: '504', pais_detectado: 'Honduras', idioma_flujo: 'ES' };\n if (n.startsWith('505')) return { country_code: '505', pais_detectado: 'Nicaragua', idioma_flujo: 'ES' };\n if (n.startsWith('506')) return { country_code: '506', pais_detectado: 'Costa Rica', idioma_flujo: 'ES' };\n if (n.startsWith('507')) return { country_code: '507', pais_detectado: 'Panamá', idioma_flujo: 'ES' };\n\n // República Dominicana: +1 809 / 829 / 849\n if (n.startsWith('1809') || n.startsWith('1829') || n.startsWith('1849')) {\n return { country_code: '1', pais_detectado: 'República Dominicana', idioma_flujo: 'ES' };\n }\n\n // Default para +1 no dominicano: inglés hasta que hagamos tabla formal\n if (n.startsWith('1')) {\n return { country_code: '1', pais_detectado: 'País +1 no identificado', idioma_flujo: 'EN' };\n }\n\n return { country_code: '', pais_detectado: 'No identificado', idioma_flujo: 'ES' };\n};\n\nconst idiomaInfo = detectarIdiomaPorTelefono(senderPhone);\n\n// --------------------------------------------------\n// 6. Resultado normalizado\n// --------------------------------------------------\n\nreturn [\n {\n json: {\n event_id: texto(key.id || data.id || Date.now()),\n fecha_recepcion: new Date().toISOString(),\n\n canal_origen: 'WHATSAPP',\n\n // Compatibilidad con el flujo actual:\n // seguimos llenando manager_telefono / manager_nombre,\n // pero ahora apuntan al sender real, no al grupo.\n manager_telefono: senderPhone,\n manager_nombre: pushName,\n usuario_id_origen: senderPhone,\n\n // Campos nuevos para grupo\n is_group: isGroup,\n group_jid: groupJid,\n group_name: texto(data.groupName || body.groupName || ''),\n sender_jid: senderJid,\n sender_phone: senderPhone,\n sender_name: pushName,\n\n whatsapp_remote_jid: remoteJid,\n whatsapp_to: whatsappTo,\n\n from_me: Boolean(key.fromMe || data.fromMe || false),\n\n message_type: messageType,\n texto: textoMensaje,\n accion_flujo: normalizarComando(textoMensaje),\n\n tiene_media: tieneMedia,\n media_count: tieneMedia ? 1 : 0,\n media_source_id: mediaSourceId,\n media_mime_type: mediaMimeType,\n media_file_name: documentFileName,\n\n country_code: idiomaInfo.country_code,\n pais_detectado: idiomaInfo.pais_detectado,\n idioma_flujo: idiomaInfo.idioma_flujo,\n\n raw_preview: JSON.stringify(body).slice(0, 4000),\n raw_event: body,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -9200, -608 ], "id": "35e401ba-acce-429c-b811-58a45c032869", "name": "Code - Normalizar evento WhatsApp TEST" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "f2be1fb5-b047-4614-a1f2-9a4d65ef5cc7", "leftValue": "={{ $json.manager_telefono === '18298946645' || $json.sender_phone === '18298946645' }}", "rightValue": "={{ $json.manager_telefono === '18298946645' }}", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ -8928, -608 ], "id": "3b65e795-a7f8-415c-8c04-7cde544c4350", "name": "Mi numero TESTING" }, { "parameters": { "httpMethod": "POST", "path": "factura", "options": {} }, "type": "n8n-nodes-base.webhook", "typeVersion": 2.1, "position": [ -9408, -608 ], "id": "bbdd46ca-d2f0-4c42-8e14-d9591168cf4d", "name": "Webhook - Evolution WhatsApp TEST", "webhookId": "e2e6c9c2-6c1e-453d-85f1-271056935828" }, { "parameters": { "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -8560, -624 ], "id": "653ca576-1661-49aa-80cb-62b7ec46a1c0", "name": "Sheets - Leer sesiones existentes", "alwaysOutputData": true, "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const evento = $('Code - Normalizar evento WhatsApp TEST').first().json || {};\nconst sesiones = $input.all().map((item) => item.json || {});\n\nconst texto = (valor) => String(valor ?? '').trim();\n\nconst estadosAbiertos = new Set([\n 'ACTIVA',\n 'PENDIENTE',\n 'EN_PROCESO',\n 'PROCESANDO',\n 'LISTO_PARA_RECUPERAR_MEDIA',\n 'LISTO_PARA_ANALIZAR',\n]);\n\nconst etapasAbiertas = new Set([\n 'ESPERANDO_AUDIO',\n 'ESPERANDO_IMAGENES',\n 'ESPERANDO_VIDEOS',\n 'PROCESANDO',\n]);\n\nconst managerTelefono = texto(evento.manager_telefono || evento.sender_phone);\nconst senderPhone = texto(evento.sender_phone || managerTelefono);\nconst groupJid = texto(evento.group_jid);\nconst isGroup = Boolean(evento.is_group);\nconst accion = texto(evento.accion_flujo || 'NORMAL');\n\n// --------------------------------------------------\n// Buscar sesión activa\n// --------------------------------------------------\n// En grupo: sesión única por grupo + sender.\n// En chat individual: sesión por manager_telefono.\nconst sesionActiva = sesiones\n .filter((row) => {\n const rowManager = texto(row.manager_telefono);\n const rowSender = texto(row.sender_phone || row.manager_telefono);\n const rowGroup = texto(row.group_jid);\n\n if (isGroup) {\n return rowGroup === groupJid && rowSender === senderPhone;\n }\n\n return rowManager === managerTelefono;\n })\n .filter((row) => {\n const estado = texto(row.estado).toUpperCase();\n const etapa = texto(row.etapa).toUpperCase();\n\n return estadosAbiertos.has(estado) || etapasAbiertas.has(etapa);\n })\n .sort((a, b) => {\n return new Date(b.ultima_actividad || b.fecha_inicio || 0) - new Date(a.ultima_actividad || a.fecha_inicio || 0);\n })[0] || null;\n\n// --------------------------------------------------\n// Crear IDs\n// --------------------------------------------------\n\nconst suffix = Math.random().toString(36).slice(2, 6).toUpperCase();\nconst phoneSuffix = senderPhone.slice(-4) || managerTelefono.slice(-4) || '0000';\nconst sessionId = `WA_${Date.now()}_${phoneSuffix}_${suffix}`;\n\nlet decision = '';\n\nif (accion === 'START') {\n decision = sesionActiva ? 'AVISO_SESION_ACTIVA' : 'CREAR_SESION';\n} else if (accion === 'CANCELAR') {\n decision = sesionActiva ? 'CANCELAR_SESION' : 'SIN_SESION_PARA_CANCELAR';\n} else {\n decision = sesionActiva ? 'CONTINUAR_SESION' : 'SIN_SESION_ACTIVA';\n}\n\nreturn [\n {\n json: {\n ...evento,\n\n sesion_activa_encontrada: Boolean(sesionActiva),\n sesion_activa: sesionActiva,\n\n session_id: sesionActiva ? texto(sesionActiva.session_id) : sessionId,\n ejecucion_id: sesionActiva ? texto(sesionActiva.ejecucion_id || sesionActiva.session_id) : sessionId,\n\n decision_flujo: decision,\n\n nueva_sesion: {\n session_id: sessionId,\n\n manager_telefono: managerTelefono,\n manager_nombre: texto(evento.manager_nombre),\n\n canal_origen: 'WHATSAPP',\n fecha_inicio: new Date().toISOString(),\n ultima_actividad: new Date().toISOString(),\n\n etapa: 'ESPERANDO_AUDIO',\n\n audio_count: 0,\n imagenes_count: 0,\n videos_count: 0,\n\n estado: 'ACTIVA',\n ejecucion_id: sessionId,\n motivo_revision: '',\n\n // Nuevos campos para grupo, idioma y clasificación futura\n is_group: isGroup,\n group_jid: groupJid,\n group_name: texto(evento.group_name),\n sender_jid: texto(evento.sender_jid),\n sender_phone: senderPhone,\n sender_name: texto(evento.sender_name),\n\n country_code: texto(evento.country_code),\n pais_detectado: texto(evento.pais_detectado),\n idioma_flujo: texto(evento.idioma_flujo || 'ES'),\n\n tipo_reporte: ''\n },\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -8368, -624 ], "id": "91675542-137d-4976-9c1c-3ba26bb1ddab", "name": "Code - Resolver sesión WhatsApp TEST" }, { "parameters": { "rules": { "values": [ { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "leftValue": "={{ $json.decision_flujo === 'CREAR_SESION' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true }, "id": "acec6506-977a-412d-b08a-fbcc8de4b0b8" } ], "combinator": "and" }, "renameOutput": true, "outputKey": "CREAR_SESION" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "5c9dcfa5-9b15-428c-ad80-c277b0671658", "leftValue": "={{ $json.decision_flujo === 'AVISO_SESION_ACTIVA' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "AVISO_SESION_ACTIVA" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "966402b5-4517-45c2-8a87-0a7aa4e5e682", "leftValue": "={{ $json.decision_flujo === 'SIN_SESION_ACTIVA' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "SIN_SESION_ACTIVA" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "698f3da3-29d2-472f-b625-af387891035e", "leftValue": "={{ $json.decision_flujo === 'CANCELAR_SESION' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "CANCELAR_SESION" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "d5feac06-4493-494e-ac1e-36c007f0de79", "leftValue": "={{ $json.decision_flujo === 'SIN_SESION_PARA_CANCELAR' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "SIN_SESION_PARA_CANCELAR" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "950979b7-b930-4260-a392-52277a742e43", "leftValue": "={{ $json.decision_flujo === 'CONTINUAR_SESION' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "CONTINUAR_SESION" } ] }, "options": {} }, "type": "n8n-nodes-base.switch", "typeVersion": 3.4, "position": [ -8144, -688 ], "id": "50d7bb39-3a6d-4906-b695-e6b09ba5c545", "name": "Switch - Decisión sesión WhatsApp TEST" }, { "parameters": { "jsCode": "const datos = $('Code - Resolver sesión WhatsApp TEST').first().json || {};\n\nconst idioma = String(\n datos.idioma_flujo ||\n datos.nueva_sesion?.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst nombre =\n datos.manager_nombre ||\n datos.sender_name ||\n 'equipo';\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '📸 *GLM Proposal Evidence Registry*',\n '',\n `Hello, ${nombre}.`,\n '',\n 'Use this flow to register evidence for Fulgencio Fumado.',\n '',\n '*Report Type:*',\n '1. *Executed Proposal*: Already implemented.',\n '2. *External Proposal*: Received outside the internal bank.',\n '',\n '*Step 1 of 3 — Voice note*',\n '',\n 'Send a voice note and clearly say if this is an *executed* or *external* proposal.',\n '',\n 'Include:',\n '• Proposal/reference',\n '• Brand/client',\n '• Country',\n '• Location',\n '• Date',\n '• What was implemented or reported',\n '• Results/comments',\n '',\n '*Important:* One proposal/report at a time.'\n ].join('\\n');\n} else {\n mensaje = [\n '📸 *Registro de Evidencias GLM*',\n '',\n `Hola, ${nombre}.`,\n '',\n 'Usa este flujo para registrar evidencias de Fulgencio Fumado.',\n '',\n '*Tipo de Reporte:*',\n '1. *Propuesta Ejecutada*: Ya implementada.',\n '2. *Propuesta Externa*: Recibida fuera del banco interno.',\n '',\n '*Paso 1 de 3 — Nota de voz*',\n '',\n 'Envía una nota de voz e indica claramente si es *ejecutada* o *externa*.',\n '',\n 'Incluye:',\n '• Propuesta/referencia',\n '• Marca/cliente',\n '• País',\n '• Ubicación',\n '• Fecha',\n '• Qué se implementó o reportó',\n '• Resultados/comentarios',\n '',\n '*Importante:* Una sola propuesta o reporte a la vez.'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...datos,\n whatsapp_to: datos.whatsapp_to || datos.group_jid || datos.whatsapp_remote_jid,\n whatsapp_text: mensaje,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5552, -2608 ], "id": "304baef0-fda7-43ce-8bfe-24ed684ced3b", "name": "Code - Preparar bienvenida WhatsApp TEST" }, { "parameters": { "jsCode": "const datos = $json || {};\n\nconst idioma = String(\n datos.idioma_flujo ||\n datos.sesion_activa?.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst etapa = String(datos.sesion_activa?.etapa || '').trim() || 'pendiente';\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '⚠️ You already have an active proposal report in progress.',\n '',\n `Current step: ${etapa}`,\n '',\n 'Please complete that report before starting a new one.',\n '',\n 'To cancel it, use the cancel option.',\n ].join('\\n');\n} else {\n mensaje = [\n '⚠️ Ya tienes un reporte de propuesta en proceso.',\n '',\n `Etapa actual: ${etapa}`,\n '',\n 'Completa ese reporte antes de iniciar uno nuevo.',\n '',\n 'Para cancelarlo, usa la opción de cancelar.',\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...datos,\n whatsapp_to: datos.whatsapp_to || datos.whatsapp_remote_jid,\n whatsapp_text: mensaje,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5520, -2160 ], "id": "bf9701ac-00d2-47c3-a495-ddaad66ed0be", "name": "Code - Preparar aviso sesión activa WhatsApp TEST" }, { "parameters": { "operation": "append", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.nueva_sesion.session_id }}", "manager_telefono": "={{ $json.nueva_sesion.manager_telefono }}", "manager_nombre": "={{ $json.nueva_sesion.manager_nombre }}", "canal_origen": "={{ $json.nueva_sesion.canal_origen }}", "fecha_inicio": "={{ $json.nueva_sesion.fecha_inicio }}", "ultima_actividad": "={{ $json.nueva_sesion.ultima_actividad }}", "etapa": "={{ $json.nueva_sesion.etapa }}", "audio_count": "={{ $json.nueva_sesion.audio_count }}", "imagenes_count": "={{ $json.nueva_sesion.imagenes_count }}", "videos_count": "={{ $json.nueva_sesion.videos_count }}", "estado": "={{ $json.nueva_sesion.estado }}", "ejecucion_id": "={{ $json.nueva_sesion.ejecucion_id }}", "motivo_revision": "={{ $json.nueva_sesion.motivo_revision }}", "is_group": "={{ $json.nueva_sesion.is_group }}", "group_jid": "={{ $json.nueva_sesion.group_jid }}", "group_name": "={{ $json.nueva_sesion.group_name }}", "sender_jid": "={{ $json.nueva_sesion.sender_jid }}", "sender_phone": "={{ $json.nueva_sesion.sender_phone }}", "sender_name": "={{ $json.nueva_sesion.sender_name }}", "country_code": "={{ $json.nueva_sesion.country_code }}", "pais_detectado": "={{ $json.nueva_sesion.pais_detectado }}", "idioma_flujo": "={{ $json.nueva_sesion.idioma_flujo }}", "tipo_reporte": "={{ $json.nueva_sesion.tipo_reporte }}" }, "matchingColumns": [], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "is_group", "displayName": "is_group", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_jid", "displayName": "group_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_name", "displayName": "group_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_jid", "displayName": "sender_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_phone", "displayName": "sender_phone", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_name", "displayName": "sender_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "country_code", "displayName": "country_code", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "pais_detectado", "displayName": "pais_detectado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "idioma_flujo", "displayName": "idioma_flujo", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "tipo_reporte", "displayName": "tipo_reporte", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -5760, -2608 ], "id": "cff69027-2e70-4be6-ad78-80ae6680ca75", "name": "Sheets - Crear sesión WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const datos = $json || {};\n\nconst texto = (valor) => String(valor ?? '').trim();\n\nconst etapa = texto(datos.sesion_activa?.etapa).toUpperCase();\nconst tipoMensaje = texto(datos.message_type).toLowerCase();\nconst accion = texto(datos.accion_flujo).toUpperCase();\n\nconst imagenesCount = Number(datos.sesion_activa?.imagenes_count || 0);\nconst videosCount = Number(datos.sesion_activa?.videos_count || 0);\n\nlet paso_decision = 'NO_DEFINIDO';\n\n// --------------------------------------------------\n// 1. Cancelación global\n// --------------------------------------------------\nif (accion === 'CANCELAR') {\n paso_decision = 'CANCELAR_SESION';\n\n// --------------------------------------------------\n// 2. Paso 1: esperando nota de voz\n// --------------------------------------------------\n} else if (etapa === 'ESPERANDO_AUDIO') {\n if (tipoMensaje === 'audio') {\n paso_decision = 'GUARDAR_AUDIO';\n } else if (tipoMensaje === 'text') {\n paso_decision = 'PEDIR_AUDIO';\n } else {\n paso_decision = 'IGNORAR_EVENTO';\n }\n\n// --------------------------------------------------\n// 3. Paso 2: esperando imágenes obligatorias\n// --------------------------------------------------\n} else if (etapa === 'ESPERANDO_IMAGENES') {\n if (tipoMensaje === 'image') {\n paso_decision = 'GUARDAR_IMAGEN';\n\n } else if (accion === 'FOTOS_LISTAS') {\n // La validación real se hace después leyendo wa_ejecuciones_eventos.\n paso_decision = 'FOTOS_LISTAS';\n\n } else if (tipoMensaje === 'text') {\n paso_decision = 'PEDIR_IMAGENES';\n\n } else {\n // Si WhatsApp manda eventos raros durante imágenes, no responder.\n paso_decision = 'IGNORAR_EVENTO';\n }\n\n// --------------------------------------------------\n// 4. Paso 3: esperando videos opcionales\n// --------------------------------------------------\n} else if (etapa === 'ESPERANDO_VIDEOS') {\n if (tipoMensaje === 'video') {\n paso_decision = 'GUARDAR_VIDEO';\n\n } else if (accion === 'SIN_VIDEO') {\n paso_decision = 'CERRAR_SIN_VIDEO';\n\n } else if (accion === 'LISTO') {\n paso_decision = 'CERRAR_CON_VIDEOS';\n\n } else if (tipoMensaje === 'text') {\n paso_decision = 'PEDIR_VIDEOS';\n\n } else {\n // Si WhatsApp manda eventos raros durante videos, no responder.\n paso_decision = 'IGNORAR_EVENTO';\n }\n\n// --------------------------------------------------\n// 5. Sesión en procesamiento\n// --------------------------------------------------\n} else if (etapa === 'PROCESANDO') {\n paso_decision = 'AVISO_PROCESANDO';\n\n// --------------------------------------------------\n// 6. Etapa no reconocida\n// --------------------------------------------------\n} else {\n paso_decision = 'ETAPA_NO_RECONOCIDA';\n}\n\nreturn [\n {\n json: {\n ...datos,\n\n etapa_actual: etapa,\n tipo_mensaje_actual: tipoMensaje,\n accion_actual: accion,\n\n imagenes_count_actual: imagenesCount,\n videos_count_actual: videosCount,\n\n paso_decision,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -7856, -208 ], "id": "28aaa567-1c39-49ac-802d-38e001d71cc2", "name": "Code - Resolver paso activo WhatsApp TEST" }, { "parameters": { "rules": { "values": [ { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "leftValue": "={{ $json.paso_decision === 'GUARDAR_AUDIO' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true }, "id": "c7fef2d7-50ce-467a-b125-30ae7fba3a58" } ], "combinator": "and" }, "renameOutput": true, "outputKey": "GUARDAR_AUDIO" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "8da042cf-54e6-460f-b505-2b17c4f4ff3c", "leftValue": "={{ $json.paso_decision === 'PEDIR_AUDIO' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "PEDIR_AUDIO" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "ecf52241-ef7e-4a9a-8823-b9e3b438da61", "leftValue": "={{ $json.paso_decision === 'GUARDAR_AUDIO' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "GUARDAR_AUDIO" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "04433553-d0ee-4391-a4f4-926197ac4c0c", "leftValue": "={{ $json.paso_decision === 'PEDIR_AUDIO' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "PEDIR_AUDIO" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "8637dc0f-59b6-40ac-8bc4-baf869829598", "leftValue": "={{ $json.paso_decision === 'GUARDAR_IMAGEN' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "GUARDAR_IMAGEN" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "4096e616-2517-48ab-b6c4-2712f763ec1d", "leftValue": "={{ $json.paso_decision === 'FOTOS_LISTAS' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "FOTOS_LISTAS" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "d1dd1d5c-8e25-4147-876e-5e7227784f6f", "leftValue": "={{ $json.paso_decision === 'PEDIR_IMAGENES' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "PEDIR_IMAGENES" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "bddbb16a-0463-47dd-9a6c-03c3635043f1", "leftValue": "={{ $json.paso_decision === 'GUARDAR_VIDEO' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "GUARDAR_VIDEO" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "fe05ba35-0ccc-417a-a4e8-b907611932e4", "leftValue": "={{ $json.paso_decision === 'CERRAR_SIN_VIDEO' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "CERRAR_SIN_VIDEO" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "94647ac0-ee80-4c96-9f25-8a847704b6a0", "leftValue": "={{ $json.paso_decision === 'CERRAR_CON_VIDEOS' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "CERRAR_CON_VIDEOS" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "3038a656-50ae-4738-b2ff-a6276bd06522", "leftValue": "={{ $json.paso_decision === 'PEDIR_VIDEOS' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "PEDIR_VIDEOS" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "1b20a524-2289-4d37-8372-9a839dadc971", "leftValue": "={{ $json.paso_decision === 'AVISO_PROCESANDO' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "AVISO_PROCESANDO" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "0943bbd0-f5b7-43c4-9b01-7f6bea244894", "leftValue": "={{ $json.paso_decision === 'ETAPA_NO_RECONOCIDA' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "ETAPA_NO_RECONOCIDA" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "84c163a3-3c08-4636-9104-c0e4c55b11e6", "leftValue": "={{ $json.paso_decision === 'IGNORAR_EVENTO' }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "IGNORAR_EVENTO" } ] }, "options": {} }, "type": "n8n-nodes-base.switch", "typeVersion": 3.4, "position": [ -7664, 64 ], "id": "b8b395eb-8f3f-4006-8ad8-d426f7e0792a", "name": "Switch - Paso activo WhatsApp TEST" }, { "parameters": { "jsCode": "const datos = $json || {};\n\nconst idioma = String(\n datos.idioma_flujo ||\n datos.sesion_activa?.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '🎙️ I still need the required voice note to start the report.',\n '',\n 'Please send a voice note including:',\n '',\n '• Proposal name or reference',\n '• Brand or client',\n '• Country',\n '• Execution/location details',\n '• Date',\n '• What was implemented or reported',\n '• Any relevant comments or observed results',\n ].join('\\n');\n} else {\n mensaje = [\n '🎙️ Aún necesito la nota de voz obligatoria para iniciar el reporte.',\n '',\n 'Por favor envía una nota de voz indicando:',\n '',\n '• Nombre o referencia de la propuesta',\n '• Marca o cliente',\n '• País',\n '• Ubicación o contexto de la ejecución',\n '• Fecha',\n '• Qué se implementó o qué se está reportando',\n '• Comentarios o resultados observados',\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...datos,\n whatsapp_to: datos.whatsapp_to || datos.whatsapp_remote_jid,\n whatsapp_text: mensaje,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5744, -528 ], "id": "30196ae5-1d82-46e3-9296-32ffa6122ae1", "name": "Code - Preparar solicitud audio WhatsApp TEST" }, { "parameters": { "operation": "append", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 306129743, "mode": "list", "cachedResultName": "wa_ejecuciones_eventos", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=306129743" }, "columns": { "mappingMode": "defineBelow", "value": { "event_id": "={{ $json.event_id }}", "fecha_recepcion": "={{ $json.fecha_recepcion }}", "manager_telefono": "={{ $json.manager_telefono }}", "manager_nombre": "={{ $json.manager_nombre }}", "message_type": "={{ $json.message_type }}", "texto": "={{ $json.texto }}", "comando": "={{ $json.accion_flujo }}", "tiene_media": "={{ $json.tiene_media }}", "media_count": "={{ $json.media_count }}", "raw_preview": "={{ $json.raw_preview }}", "estado": "={{ $json.estado }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "fecha_procesado": "={{ $json.fecha_procesado }}", "session_id": "={{ $json.session_id }}", "canal_origen": "={{ $json.canal_origen }}", "etapa_recibida": "={{ $json.etapa_recibida }}", "media_source_id": "={{ $json.media_source_id }}", "media_mime_type": "={{ $json.media_mime_type }}", "media_file_name": "={{ $json.media_file_name }}", "whatsapp_remote_jid": "={{ $json.whatsapp_remote_jid }}" }, "matchingColumns": [], "schema": [ { "id": "event_id", "displayName": "event_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_recepcion", "displayName": "fecha_recepcion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "message_type", "displayName": "message_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "texto", "displayName": "texto", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "comando", "displayName": "comando", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "tiene_media", "displayName": "tiene_media", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_count", "displayName": "media_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "raw_preview", "displayName": "raw_preview", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_procesado", "displayName": "fecha_procesado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa_recibida", "displayName": "etapa_recibida", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_source_id", "displayName": "media_source_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "media_mime_type", "displayName": "media_mime_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "media_file_name", "displayName": "media_file_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "whatsapp_remote_jid", "displayName": "whatsapp_remote_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -6032, -912 ], "id": "4b7309e9-5602-4c88-b29f-42e44179f445", "name": "Sheets - Guardar evento audio WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const datos = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\nconst sesion = datos.sesion_activa || datos.nueva_sesion || {};\n\nreturn [\n {\n json: {\n ...datos,\n\n session_id: datos.session_id,\n ultima_actividad: new Date().toISOString(),\n\n etapa: 'ESPERANDO_IMAGENES',\n estado: 'ACTIVA',\n\n audio_count: Number(sesion.audio_count || 0) + 1,\n imagenes_count: Number(sesion.imagenes_count || 0),\n videos_count: Number(sesion.videos_count || 0),\n\n manager_telefono: datos.manager_telefono || sesion.manager_telefono || datos.sender_phone || '',\n manager_nombre: datos.manager_nombre || sesion.manager_nombre || datos.sender_name || '',\n\n is_group: datos.is_group ?? sesion.is_group ?? false,\n group_jid: datos.group_jid || sesion.group_jid || '',\n group_name: datos.group_name || sesion.group_name || '',\n sender_jid: datos.sender_jid || sesion.sender_jid || '',\n sender_phone: datos.sender_phone || sesion.sender_phone || datos.manager_telefono || '',\n sender_name: datos.sender_name || sesion.sender_name || datos.manager_nombre || '',\n\n country_code: datos.country_code || sesion.country_code || '',\n pais_detectado: datos.pais_detectado || sesion.pais_detectado || '',\n idioma_flujo: datos.idioma_flujo || sesion.idioma_flujo || 'ES',\n\n tipo_reporte: sesion.tipo_reporte || datos.tipo_reporte || '',\n\n whatsapp_to: datos.whatsapp_to || datos.group_jid || datos.whatsapp_remote_jid || ''\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5824, -912 ], "id": "25599b76-fe73-44b6-80f5-1d8ba9616fd1", "name": "Code - Preparar actualización audio recibido WhatsApp TEST" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ultima_actividad": "={{ $json.ultima_actividad }}", "etapa": "={{ $json.etapa }}", "estado": "={{ $json.estado }}", "audio_count": "={{ $json.audio_count }}", "imagenes_count": "={{ $json.imagenes_count }}", "videos_count": "={{ $json.videos_count }}", "is_group": "={{ $json.is_group }}", "group_jid": "={{ $json.group_jid }}", "group_name": "={{ $json.group_name }}", "sender_jid": "={{ $json.sender_jid }}", "sender_phone": "={{ $json.sender_phone }}", "sender_name": "={{ $json.sender_name }}", "country_code": "={{ $json.country_code }}", "pais_detectado": "={{ $json.pais_detectado }}", "idioma_flujo": "={{ $json.idioma_flujo }}", "tipo_reporte": "={{ $json.tipo_reporte }}", "manager_telefono": "={{ $json.manager_telefono }}", "manager_nombre": "={{ $json.manager_nombre }}", "ejecucion_id": "={{ $json.ejecucion_id }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "is_group", "displayName": "is_group", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_jid", "displayName": "group_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_name", "displayName": "group_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_jid", "displayName": "sender_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_phone", "displayName": "sender_phone", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_name", "displayName": "sender_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "country_code", "displayName": "country_code", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "pais_detectado", "displayName": "pais_detectado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "idioma_flujo", "displayName": "idioma_flujo", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "tipo_reporte", "displayName": "tipo_reporte", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -5616, -912 ], "id": "35b5002d-7c69-4f31-89de-dc5457961a42", "name": "Sheets - Actualizar sesión audio recibido WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const datos = $('Code - Preparar actualización audio recibido WhatsApp TEST').first().json || {};\n\nconst idioma = String(\n datos.idioma_flujo ||\n datos.sesion_activa?.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '✅ Voice note received.',\n '',\n '*Step 2 of 3 — Required images*',\n '',\n 'Now send at least one photo related to the proposal or report.',\n '',\n '⚠️ Important: send images one by one, in separate messages.',\n 'Do not send multiple images together in the same WhatsApp album/message.',\n '',\n 'You can send multiple images, but each one must be sent separately.',\n '',\n 'When you finish sending images, write: PHOTOS READY'\n ].join('\\n');\n} else {\n mensaje = [\n '✅ Nota de voz recibida.',\n '',\n '*Paso 2 de 3 — Imágenes obligatorias*',\n '',\n 'Ahora envía al menos una foto relacionada a la propuesta o reporte.',\n '',\n '⚠️ Importante: envía las imágenes una por una, en mensajes separados.',\n 'No envíes varias imágenes juntas en el mismo álbum/mensaje de WhatsApp.',\n '',\n 'Puedes enviar varias imágenes, pero cada una debe ir por separado.',\n '',\n 'Cuando termines de enviar las imágenes, escribe: FOTOS LISTAS'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...datos,\n whatsapp_to: datos.whatsapp_to || datos.group_jid || datos.whatsapp_remote_jid,\n whatsapp_text: mensaje,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5392, -912 ], "id": "7c1c09aa-4670-47d0-a5a5-3713adf6b6fb", "name": "Code - Preparar solicitud imágenes WhatsApp TEST" }, { "parameters": { "jsCode": "const datos = $json || {};\n\nreturn [\n {\n json: {\n ...datos,\n session_id: datos.session_id,\n ultima_actividad: new Date().toISOString(),\n etapa: 'CANCELADA',\n estado: 'CANCELADA',\n motivo_revision: 'CANCELADO_POR_USUARIO',\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5648, -1344 ], "id": "dd118cad-907f-4bab-89ef-8d75dc2d70cb", "name": "Code - Preparar cancelación sesión WhatsApp TEST" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ultima_actividad": "={{ $json.ultima_actividad }}", "etapa": "={{ $json.etapa }}", "estado": "={{ $json.estado }}", "motivo_revision": "={{ $json.motivo_revision }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -5440, -1344 ], "id": "ac83d38f-7d91-408a-bb43-f8950ccc40a6", "name": "Sheets - Cancelar sesión WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const actual = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst base = getNodeJson('Code - Resolver sesión WhatsApp TEST');\n\nconst sesion =\n base.sesion_activa ||\n actual.sesion_activa ||\n {};\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst idioma = limpiar(\n actual.idioma_flujo ||\n base.idioma_flujo ||\n sesion.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst destinoBase = limpiar(\n base.whatsapp_to ||\n base.group_jid ||\n actual.whatsapp_to ||\n actual.group_jid ||\n base.whatsapp_remote_jid ||\n actual.whatsapp_remote_jid ||\n sesion.group_jid ||\n sesion.whatsapp_to ||\n sesion.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n actual.manager_telefono ||\n base.sender_phone ||\n actual.sender_phone ||\n ''\n);\n\nif (!destinoBase) {\n throw new Error('No se encontró destino WhatsApp para enviar mensaje de cancelación.');\n}\n\nlet whatsappTo = destinoBase;\n\nif (whatsappTo.includes('@g.us')) {\n whatsappTo = whatsappTo;\n} else if (whatsappTo.includes('@s.whatsapp.net')) {\n whatsappTo = whatsappTo;\n} else {\n whatsappTo = `${whatsappTo.replace(/\\D/g, '')}@s.whatsapp.net`;\n}\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '✅ *Report cancelled successfully.*',\n '',\n 'The current report was closed and no additional evidence will be added to that session.',\n '',\n 'When you are ready, you can start a new registration by writing:',\n '',\n '*Hey*'\n ].join('\\n');\n} else {\n mensaje = [\n '✅ *Reporte cancelado correctamente.*',\n '',\n 'El reporte actual fue cerrado y no se agregará más evidencia a esa sesión.',\n '',\n 'Cuando estés listo, puedes iniciar un nuevo registro escribiendo:',\n '',\n '*Hey*'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...base,\n ...actual,\n\n whatsapp_to: whatsappTo,\n whatsapp_text: mensaje\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5184, -1408 ], "id": "119da00c-cea5-4184-90e5-ad4a408271bb", "name": "Code - Preparar mensaje cancelación WhatsApp TEST" }, { "parameters": { "jsCode": "const evento = $json || {};\nconst base = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\nconst sesion = base.sesion_activa || base.nueva_sesion || {};\n\nconst ahora = new Date().toISOString();\n\nconst imagenesActuales = Number(sesion.imagenes_count || 0);\n\nreturn [\n {\n json: {\n ...base,\n ...evento,\n\n session_id: base.session_id || sesion.session_id || evento.session_id || '',\n ejecucion_id: sesion.ejecucion_id || base.ejecucion_id || base.session_id || '',\n\n manager_telefono: base.manager_telefono || sesion.manager_telefono || evento.manager_telefono || base.sender_phone || '',\n manager_nombre: base.manager_nombre || sesion.manager_nombre || evento.manager_nombre || base.sender_name || '',\n\n canal_origen: base.canal_origen || sesion.canal_origen || 'WHATSAPP',\n\n fecha_inicio: sesion.fecha_inicio || base.fecha_inicio || '',\n ultima_actividad: ahora,\n\n etapa: 'ESPERANDO_IMAGENES',\n\n audio_count: Number(sesion.audio_count || 0),\n imagenes_count: imagenesActuales + 1,\n videos_count: Number(sesion.videos_count || 0),\n\n estado: sesion.estado || 'ACTIVA',\n motivo_revision: sesion.motivo_revision || '',\n\n is_group: base.is_group ?? sesion.is_group ?? false,\n group_jid: base.group_jid || sesion.group_jid || '',\n group_name: base.group_name || sesion.group_name || '',\n\n sender_jid: base.sender_jid || sesion.sender_jid || '',\n sender_phone: base.sender_phone || sesion.sender_phone || base.manager_telefono || '',\n sender_name: base.sender_name || sesion.sender_name || base.manager_nombre || '',\n\n country_code: base.country_code || sesion.country_code || '',\n pais_detectado: base.pais_detectado || sesion.pais_detectado || '',\n idioma_flujo: base.idioma_flujo || sesion.idioma_flujo || 'ES',\n\n tipo_reporte: sesion.tipo_reporte || base.tipo_reporte || '',\n\n whatsapp_to: base.whatsapp_to || base.group_jid || base.whatsapp_remote_jid || ''\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -4752, -320 ], "id": "dee83381-1061-405c-978f-33403362f74b", "name": "Code — Preparar actualización imagen recibida" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "estado": "={{ $json.estado }}", "videos_count": "={{ $json.videos_count }}", "imagenes_count": "={{ $json.imagenes_count }}", "audio_count": "={{ $json.audio_count }}", "etapa": "={{ $json.etapa }}", "ultima_actividad": "={{ $json.ultima_actividad }}", "is_group": "={{ $json.is_group }}", "group_jid": "={{ $json.group_jid }}", "group_name": "={{ $json.group_name }}", "sender_jid": "={{ $json.sender_jid }}", "sender_phone": "={{ $json.sender_phone }}", "sender_name": "={{ $json.sender_name }}", "country_code": "={{ $json.country_code }}", "pais_detectado": "={{ $json.pais_detectado }}", "idioma_flujo": "={{ $json.idioma_flujo }}", "tipo_reporte": "={{ $json.tipo_reporte }}", "manager_telefono": "={{ $json.manager_telefono }}", "manager_nombre": "={{ $json.manager_nombre }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "is_group", "displayName": "is_group", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_jid", "displayName": "group_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_name", "displayName": "group_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_jid", "displayName": "sender_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_phone", "displayName": "sender_phone", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_name", "displayName": "sender_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "country_code", "displayName": "country_code", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "pais_detectado", "displayName": "pais_detectado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "idioma_flujo", "displayName": "idioma_flujo", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "tipo_reporte", "displayName": "tipo_reporte", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -4544, -320 ], "id": "392c7a6c-3ab8-4f56-8742-8ab4ffdd5a4c", "name": "Sheets — Actualizar sesión imagen recibida", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const datos = $json || {};\n\nconst idioma = String(\n datos.idioma_flujo ||\n datos.sesion_activa?.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '✅ Image received.',\n '',\n 'You can send more images if needed.',\n '',\n '⚠️ Remember: send each image in a separate message.',\n 'Do not send multiple images together.',\n '',\n 'When you finish sending images, write: PHOTOS READY'\n ].join('\\n');\n} else {\n mensaje = [\n '✅ Imagen recibida.',\n '',\n 'Puedes enviar más imágenes si hace falta.',\n '',\n '⚠️ Recuerda: envía cada imagen en un mensaje separado.',\n 'No envíes varias imágenes juntas.',\n '',\n 'Cuando termines de enviar las imágenes, escribe: FOTOS LISTAS'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...datos,\n whatsapp_to: datos.whatsapp_to || datos.group_jid || datos.whatsapp_remote_jid,\n whatsapp_text: mensaje\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -4336, -320 ], "id": "ebc7dadc-6ddc-4806-8c19-7d4e2ce567cf", "name": "Code — Preparar confirmación imagen" }, { "parameters": { "operation": "append", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 306129743, "mode": "list", "cachedResultName": "wa_ejecuciones_eventos", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=306129743" }, "columns": { "mappingMode": "defineBelow", "value": { "event_id": "={{ $json.event_id }}", "manager_telefono": "={{ $json.manager_telefono }}", "manager_nombre": "={{ $json.manager_nombre }}", "tiene_media": "={{ $json.tiene_media }}", "estado": "=IMAGEN_RECIBIDA", "session_id": "={{ $json.session_id }}", "canal_origen": "=WHATSAPP", "fecha_recepcion": "={{ $json.fecha_recepcion }}", "message_type": "={{ $json.message_type }}", "texto": "={{ $json.texto }}", "media_count": "={{ $json.media_count }}", "raw_preview": "={{ $json.raw_preview }}", "etapa_recibida": "={{ $json.etapa_actual }}", "media_source_id": "={{ $json.media_source_id }}", "media_mime_type": "={{ $json.media_mime_type }}", "media_file_name": "={{ $json.media_file_name }}", "whatsapp_remote_jid": "={{ $json.whatsapp_remote_jid }}", "comando": "={{ $json.accion_flujo }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "fecha_procesado": "={{ new Date().toISOString() }}" }, "matchingColumns": [], "schema": [ { "id": "event_id", "displayName": "event_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_recepcion", "displayName": "fecha_recepcion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "message_type", "displayName": "message_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "texto", "displayName": "texto", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "comando", "displayName": "comando", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "tiene_media", "displayName": "tiene_media", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_count", "displayName": "media_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "raw_preview", "displayName": "raw_preview", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_procesado", "displayName": "fecha_procesado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa_recibida", "displayName": "etapa_recibida", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_source_id", "displayName": "media_source_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "media_mime_type", "displayName": "media_mime_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "media_file_name", "displayName": "media_file_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "whatsapp_remote_jid", "displayName": "whatsapp_remote_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -5024, -320 ], "id": "f8cf4436-cf35-45d6-acc7-7adfb9e3a2dc", "name": "Sheets - Guardar evento imagen WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const actual = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst base = getNodeJson('Code - Resolver paso activo WhatsApp TEST');\nconst sesion = base.sesion_activa || actual.sesion_activa || {};\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst normalizarDestino = (valor) => {\n let raw = limpiar(valor);\n\n if (!raw) return '';\n\n raw = raw.replace('@c.us', '@s.whatsapp.net');\n\n if (raw.includes('@g.us')) return raw;\n\n if (raw.includes('@s.whatsapp.net')) {\n const numero = raw.replace('@s.whatsapp.net', '').replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n }\n\n const numero = raw.replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n};\n\nconst idioma = limpiar(\n actual.idioma_flujo ||\n base.idioma_flujo ||\n sesion.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst destinoBase =\n base.whatsapp_to ||\n base.group_jid ||\n actual.whatsapp_to ||\n actual.group_jid ||\n base.whatsapp_remote_jid ||\n actual.whatsapp_remote_jid ||\n sesion.group_jid ||\n sesion.whatsapp_to ||\n sesion.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n actual.manager_telefono ||\n base.sender_phone ||\n actual.sender_phone ||\n '';\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '📸 I still need at least one image.',\n '',\n 'The voice note was already received, but one evidence photo is required to complete the report.',\n '',\n '⚠️ Please send at least one image, one per message.',\n 'Do not send multiple images together.',\n '',\n 'When you finish sending images, write: PHOTOS READY'\n ].join('\\n');\n} else {\n mensaje = [\n '📸 Necesito que me envíes al menos una imagen.',\n '',\n 'La nota de voz ya fue recibida, pero para completar el reporte hace falta una foto de evidencia.',\n '',\n '⚠️ Por favor envía al menos una imagen, una por mensaje.',\n 'No envíes varias imágenes juntas.',\n '',\n 'Cuando termines de enviar las imágenes, escribe: FOTOS LISTAS'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...base,\n ...actual,\n\n whatsapp_to: normalizarDestino(destinoBase),\n whatsapp_text: mensaje\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5456, 208 ], "id": "75998ea0-d2ec-4614-b18e-824019627d72", "name": "Code - Preparar aviso falta imagen WhatsApp TEST" }, { "parameters": { "jsCode": "const base = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\nconst sesion = base.sesion_activa || base.nueva_sesion || {};\n\nconst rows = $input.all().map(item => item.json || {});\n\nconst texto = (valor) => String(valor ?? '').trim();\n\nconst normalizarNumero = (valor) =>\n texto(valor)\n .replace('@s.whatsapp.net', '')\n .replace('@c.us', '')\n .replace('@g.us', '')\n .replace('@lid', '')\n .replace(/\\D/g, '');\n\nconst sessionId = texto(base.session_id || sesion.session_id);\nconst ejecucionId = texto(sesion.ejecucion_id || base.ejecucion_id || sessionId);\n\nconst ahora = new Date().toISOString();\n\nconst fechaInicioSesion = texto(\n sesion.fecha_inicio ||\n base.fecha_inicio ||\n base.sesion_activa?.fecha_inicio ||\n ''\n);\n\nconst fechaInicioMs = fechaInicioSesion\n ? new Date(fechaInicioSesion).getTime()\n : 0;\n\nconst fechaAhoraMs = Date.now();\n\nconst groupJid = texto(\n base.group_jid ||\n sesion.group_jid ||\n base.whatsapp_remote_jid ||\n ''\n);\n\nconst managerTelefono = normalizarNumero(\n base.manager_telefono ||\n base.sender_phone ||\n sesion.manager_telefono ||\n sesion.sender_phone ||\n ''\n);\n\n// Ventana máxima de seguridad para esta sesión.\n// Así no mezclamos fotos viejas del mismo grupo/persona.\n// 45 minutos es suficiente para una ejecución normal.\nconst ventanaMaximaMs = 45 * 60 * 1000;\n\nconst imagenesUnicas = [];\nconst vistos = new Set();\n\nfor (const row of rows) {\n const tipo = texto(row.message_type).toLowerCase();\n\n if (tipo !== 'image') continue;\n\n const rowSessionId = texto(row.session_id);\n const rowGroupJid = texto(row.whatsapp_remote_jid);\n const rowManager = normalizarNumero(row.manager_telefono || row.sender_phone || '');\n\n const rowFecha = texto(row.fecha_recepcion || row.fecha_procesado || '');\n const rowFechaMs = rowFecha ? new Date(rowFecha).getTime() : 0;\n\n const mismaSesion = sessionId && rowSessionId === sessionId;\n\n const mismoGrupoYManager =\n groupJid &&\n rowGroupJid === groupJid &&\n managerTelefono &&\n rowManager === managerTelefono;\n\n const dentroDeVentana =\n rowFechaMs &&\n fechaInicioMs &&\n rowFechaMs >= fechaInicioMs - 15000 &&\n rowFechaMs <= fechaAhoraMs + 15000 &&\n rowFechaMs - fechaInicioMs <= ventanaMaximaMs;\n\n const perteneceALaEjecucion =\n mismaSesion ||\n (mismoGrupoYManager && dentroDeVentana);\n\n if (!perteneceALaEjecucion) continue;\n\n const mediaId = texto(row.media_source_id || row.event_id);\n\n if (!mediaId) continue;\n if (vistos.has(mediaId)) continue;\n\n vistos.add(mediaId);\n imagenesUnicas.push(row);\n}\n\nconst countSesion = Number(sesion.imagenes_count || base.imagenes_count_actual || 0);\n\nconst imagenesCountReal = imagenesUnicas.length > 0\n ? imagenesUnicas.length\n : countSesion;\n\nconst idsImagenes = imagenesUnicas\n .map(row => texto(row.media_source_id || row.event_id))\n .filter(Boolean);\n\nreturn [\n {\n json: {\n ...base,\n\n session_id: sessionId,\n ejecucion_id: ejecucionId,\n\n ultima_actividad: ahora,\n\n etapa: 'ESPERANDO_VIDEOS',\n estado: 'ACTIVA',\n\n audio_count: Number(sesion.audio_count || 0),\n imagenes_count: imagenesCountReal,\n videos_count: Number(sesion.videos_count || 0),\n\n manager_telefono: base.manager_telefono || sesion.manager_telefono || base.sender_phone || '',\n manager_nombre: base.manager_nombre || sesion.manager_nombre || base.sender_name || '',\n\n is_group: base.is_group ?? sesion.is_group ?? false,\n group_jid: base.group_jid || sesion.group_jid || '',\n group_name: base.group_name || sesion.group_name || '',\n\n sender_jid: base.sender_jid || sesion.sender_jid || '',\n sender_phone: base.sender_phone || sesion.sender_phone || base.manager_telefono || '',\n sender_name: base.sender_name || sesion.sender_name || base.manager_nombre || '',\n\n country_code: base.country_code || sesion.country_code || '',\n pais_detectado: base.pais_detectado || sesion.pais_detectado || '',\n idioma_flujo: base.idioma_flujo || sesion.idioma_flujo || 'ES',\n\n tipo_reporte: sesion.tipo_reporte || base.tipo_reporte || '',\n\n whatsapp_to: base.whatsapp_to || base.group_jid || base.whatsapp_remote_jid || '',\n\n imagenes_eventos_detectados: imagenesUnicas.length,\n imagenes_media_source_ids: idsImagenes.join(','),\n\n debug_fotos_listas: {\n session_id_actual: sessionId,\n fecha_inicio_sesion: fechaInicioSesion,\n group_jid: groupJid,\n manager_telefono: managerTelefono,\n total_rows_leidas: rows.length,\n total_imagenes_detectadas: imagenesUnicas.length,\n ids_imagenes: idsImagenes\n }\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5120, -16 ], "id": "b0e3e960-8cc9-453e-bbf2-4e410ab4a178", "name": "Preparar actualización fotos listas" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ultima_actividad": "={{ $json.ultima_actividad }}", "etapa": "={{ $json.etapa }}", "audio_count": "={{ $json.audio_count }}", "imagenes_count": "={{ $json.imagenes_count }}", "videos_count": "={{ $json.videos_count }}", "estado": "={{ $json.estado }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "motivo_revision": "={{ $json.motivo_revision }}", "is_group": "={{ $json.is_group }}", "group_jid": "={{ $json.group_jid }}", "group_name": "={{ $json.group_name }}", "sender_jid": "={{ $json.sender_jid }}", "sender_phone": "={{ $json.sender_phone }}", "sender_name": "={{ $json.sender_name }}", "country_code": "={{ $json.country_code }}", "pais_detectado": "={{ $json.pais_detectado }}", "idioma_flujo": "={{ $json.idioma_flujo }}", "tipo_reporte": "={{ $json.tipo_reporte }}", "manager_telefono": "={{ $json.manager_telefono }}", "manager_nombre": "={{ $json.manager_nombre }}", "canal_origen": "={{ $json.canal_origen }}", "fecha_inicio": "={{ $json.fecha_inicio }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "is_group", "displayName": "is_group", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_jid", "displayName": "group_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_name", "displayName": "group_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_jid", "displayName": "sender_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_phone", "displayName": "sender_phone", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_name", "displayName": "sender_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "country_code", "displayName": "country_code", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "pais_detectado", "displayName": "pais_detectado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "idioma_flujo", "displayName": "idioma_flujo", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "tipo_reporte", "displayName": "tipo_reporte", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -4928, -16 ], "id": "dea8b556-e019-40b3-97fc-04e1a47c1ed6", "name": "Sheets - Actualizar sesión fotos listas WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const actual = $json || {};\nconst base = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\nconst sesion = base.sesion_activa || actual.sesion_activa || {};\n\nconst idioma = String(\n actual.idioma_flujo ||\n base.idioma_flujo ||\n sesion.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst destinoBase = String(\n base.whatsapp_to ||\n base.group_jid ||\n actual.whatsapp_to ||\n actual.group_jid ||\n base.whatsapp_remote_jid ||\n actual.whatsapp_remote_jid ||\n sesion.group_jid ||\n sesion.whatsapp_to ||\n sesion.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n actual.manager_telefono ||\n ''\n).trim();\n\nif (!destinoBase) {\n throw new Error('No se encontró destino WhatsApp para enviar solicitud de videos.');\n}\n\nlet whatsappTo = destinoBase;\n\nif (whatsappTo.includes('@g.us')) {\n whatsappTo = whatsappTo;\n} else if (whatsappTo.includes('@s.whatsapp.net')) {\n whatsappTo = whatsappTo;\n} else {\n whatsappTo = `${whatsappTo.replace(/\\D/g, '')}@s.whatsapp.net`;\n}\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '*Step 3 of 3 — Optional videos*',\n '',\n 'You can send one or more videos of the execution if you have them.',\n '',\n '⚠️ Important: send videos one by one, in separate messages.',\n 'Do not send multiple videos together.',\n '',\n 'If you do not have videos, write: NO VIDEO',\n '',\n 'If you already sent videos and finished, write: DONE',\n ].join('\\n');\n} else {\n mensaje = [\n '*Paso 3 de 3 — Videos opcionales*',\n '',\n 'Puedes enviar uno o varios videos de la ejecución si tienes.',\n '',\n '⚠️ Importante: envía los videos uno por uno, en mensajes separados.',\n 'No envíes varios videos juntos.',\n '',\n 'Si no tienes videos, escribe: SIN VIDEO',\n '',\n 'Si enviaste videos y ya terminaste, escribe: LISTO',\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...base,\n ...actual,\n\n session_id: actual.session_id || base.session_id || sesion.session_id || '',\n ejecucion_id: actual.ejecucion_id || base.ejecucion_id || sesion.ejecucion_id || '',\n\n manager_telefono: actual.manager_telefono || base.manager_telefono || sesion.manager_telefono || '',\n manager_nombre: actual.manager_nombre || base.manager_nombre || sesion.manager_nombre || '',\n\n is_group: actual.is_group ?? base.is_group ?? sesion.is_group ?? false,\n group_jid: actual.group_jid || base.group_jid || sesion.group_jid || '',\n sender_phone: actual.sender_phone || base.sender_phone || sesion.sender_phone || '',\n idioma_flujo: idioma,\n\n whatsapp_to: whatsappTo,\n whatsapp_text: mensaje,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -4704, -16 ], "id": "50b9bdba-c99b-4150-9a85-e57c18e24f5a", "name": "Code - Preparar solicitud videos WhatsApp TEST" }, { "parameters": { "jsCode": "const actual = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst base = getNodeJson('Code - Resolver paso activo WhatsApp TEST');\nconst sesion = base.sesion_activa || actual.sesion_activa || {};\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst normalizarDestino = (valor) => {\n let raw = limpiar(valor);\n\n if (!raw) return '';\n\n raw = raw.replace('@c.us', '@s.whatsapp.net');\n\n if (raw.includes('@g.us')) return raw;\n\n if (raw.includes('@s.whatsapp.net')) {\n const numero = raw.replace('@s.whatsapp.net', '').replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n }\n\n const numero = raw.replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n};\n\nconst idioma = limpiar(\n actual.idioma_flujo ||\n base.idioma_flujo ||\n sesion.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst destinoBase =\n base.whatsapp_to ||\n base.group_jid ||\n actual.whatsapp_to ||\n actual.group_jid ||\n base.whatsapp_remote_jid ||\n actual.whatsapp_remote_jid ||\n sesion.group_jid ||\n sesion.whatsapp_to ||\n sesion.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n actual.manager_telefono ||\n base.sender_phone ||\n actual.sender_phone ||\n '';\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '🎥 We are already on the optional videos step.',\n '',\n 'You can send one or more videos of the execution.',\n '',\n '⚠️ Remember: send each video in a separate message.',\n 'Do not send multiple videos together.',\n '',\n 'If you do not have videos, write: *NO VIDEO*',\n '',\n 'If you already sent videos and finished, write: *DONE*'\n ].join('\\n');\n} else {\n mensaje = [\n '🎥 Ya estamos en el paso de videos opcionales.',\n '',\n 'Puedes enviar uno o varios videos de la ejecución.',\n '',\n '⚠️ Recuerda: envía cada video en un mensaje separado.',\n 'No envíes varios videos juntos.',\n '',\n 'Si no tienes videos, escribe: *SIN VIDEO*',\n '',\n 'Si ya enviaste videos y terminaste, escribe: *LISTO*'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...base,\n ...actual,\n\n whatsapp_to: normalizarDestino(destinoBase),\n whatsapp_text: mensaje\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5008, 1520 ], "id": "08b3a87f-00c6-439b-bcbe-131d86d100d5", "name": "Code - Preparar recordatorio videos WhatsApp TEST" }, { "parameters": { "jsCode": "const base = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\nconst sesion = base.sesion_activa || {};\n\nconst ahora = new Date().toISOString();\n\nconst telefonoBase = String(\n base.whatsapp_to ||\n base.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n ''\n).trim();\n\nconst whatsappTo = telefonoBase.includes('@s.whatsapp.net')\n ? telefonoBase\n : `${telefonoBase.replace(/\\D/g, '')}@s.whatsapp.net`;\n\nreturn [\n {\n json: {\n ...base,\n\n session_id: base.session_id || sesion.session_id || '',\n manager_telefono: sesion.manager_telefono || base.manager_telefono || '',\n manager_nombre: sesion.manager_nombre || base.manager_nombre || '',\n canal_origen: sesion.canal_origen || base.canal_origen || 'WHATSAPP',\n\n fecha_inicio: sesion.fecha_inicio || '',\n ultima_actividad: ahora,\n\n etapa: 'PROCESANDO',\n estado: 'LISTO_PARA_RECUPERAR_MEDIA',\n\n audio_count: Number(sesion.audio_count || 0),\n imagenes_count: Number(sesion.imagenes_count || 0),\n videos_count: Number(sesion.videos_count || 0),\n\n ejecucion_id: sesion.ejecucion_id || base.ejecucion_id || base.session_id || '',\n motivo_revision: 'SIN_VIDEO_REPORTADO',\n\n whatsapp_to: whatsappTo\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -6432, 512 ], "id": "c4db0aed-c18e-429c-a6ff-299bd94d2797", "name": "CERRAR_SIN_VIDEO" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ultima_actividad": "={{ $json.ultima_actividad }}", "etapa": "={{ $json.etapa }}", "audio_count": "={{ $json.audio_count }}", "imagenes_count": "={{ $json.imagenes_count }}", "videos_count": "={{ $json.videos_count }}", "estado": "={{ $json.estado }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "motivo_revision": "={{ $json.motivo_revision }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -6224, 512 ], "id": "27195f01-98fa-4772-b0c6-d82b8241cc83", "name": "Sheets - Actualizar sesión cierre sin video WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "operation": "append", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 306129743, "mode": "list", "cachedResultName": "wa_ejecuciones_eventos", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=306129743" }, "columns": { "mappingMode": "defineBelow", "value": { "event_id": "={{ $json.event_id }}", "fecha_recepcion": "={{ $json.fecha_recepcion }}", "manager_telefono": "={{ $json.manager_telefono }}", "manager_nombre": "={{ $json.manager_nombre }}", "message_type": "={{ $json.message_type }}", "texto": "={{ $json.texto }}", "comando": "={{ $json.accion_flujo }}", "tiene_media": "={{ $json.tiene_media }}", "media_count": "={{ $json.media_count }}", "raw_preview": "={{ $json.raw_preview }}", "estado": "=VIDEO_RECIBIDO", "ejecucion_id": "={{ $json.ejecucion_id }}", "session_id": "={{ $json.session_id }}", "canal_origen": "={{ $json.canal_origen }}", "etapa_recibida": "={{ $json.etapa_actual }}", "media_source_id": "={{ $json.media_source_id }}", "media_mime_type": "={{ $json.media_mime_type }}", "media_file_name": "={{ $json.media_file_name }}", "whatsapp_remote_jid": "={{ $json.whatsapp_remote_jid }}" }, "matchingColumns": [], "schema": [ { "id": "event_id", "displayName": "event_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_recepcion", "displayName": "fecha_recepcion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "message_type", "displayName": "message_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "texto", "displayName": "texto", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "comando", "displayName": "comando", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "tiene_media", "displayName": "tiene_media", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_count", "displayName": "media_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "raw_preview", "displayName": "raw_preview", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_procesado", "displayName": "fecha_procesado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa_recibida", "displayName": "etapa_recibida", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_source_id", "displayName": "media_source_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "media_mime_type", "displayName": "media_mime_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "media_file_name", "displayName": "media_file_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "whatsapp_remote_jid", "displayName": "whatsapp_remote_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -5312, 384 ], "id": "c98b3d5e-cf77-46c6-9d5e-2b6222949656", "name": "Sheets - Guardar evento video WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const evento = $json || {};\nconst base = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\nconst sesion = base.sesion_activa || {};\n\nconst ahora = new Date().toISOString();\n\nconst videosActuales = Number(sesion.videos_count || 0);\n\nconst telefonoBase = String(\n base.whatsapp_to ||\n base.whatsapp_remote_jid ||\n evento.whatsapp_to ||\n evento.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n ''\n).trim();\n\nconst whatsappTo = telefonoBase.includes('@s.whatsapp.net')\n ? telefonoBase\n : `${telefonoBase.replace(/\\D/g, '')}@s.whatsapp.net`;\n\nreturn [\n {\n json: {\n ...base,\n ...evento,\n\n session_id: base.session_id || sesion.session_id || evento.session_id || '',\n manager_telefono: sesion.manager_telefono || base.manager_telefono || evento.manager_telefono || '',\n manager_nombre: sesion.manager_nombre || base.manager_nombre || evento.manager_nombre || '',\n canal_origen: sesion.canal_origen || base.canal_origen || 'WHATSAPP',\n\n fecha_inicio: sesion.fecha_inicio || '',\n ultima_actividad: ahora,\n\n // Seguimos en videos hasta que el usuario escriba LISTO\n etapa: 'ESPERANDO_VIDEOS',\n\n audio_count: Number(sesion.audio_count || 0),\n imagenes_count: Number(sesion.imagenes_count || 0),\n videos_count: videosActuales + 1,\n\n estado: sesion.estado || 'ACTIVA',\n ejecucion_id: sesion.ejecucion_id || base.ejecucion_id || base.session_id || '',\n motivo_revision: sesion.motivo_revision || '',\n\n whatsapp_to: whatsappTo\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5104, 384 ], "id": "7bc3bb63-803e-43e9-9df7-1ed53a7597cb", "name": "Code - Preparar actualización video recibido WhatsApp TEST" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "motivo_revision": "={{ $json.motivo_revision }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "estado": "={{ $json.estado }}", "videos_count": "={{ $json.videos_count }}", "imagenes_count": "={{ $json.imagenes_count }}", "audio_count": "={{ $json.audio_count }}", "etapa": "={{ $json.etapa }}", "ultima_actividad": "={{ $json.ultima_actividad }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -4896, 384 ], "id": "27556223-2f6f-464f-819a-a035a66e3aad", "name": "Sheets - Actualizar sesión video recibido WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const actual = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst base = getNodeJson('Code - Resolver paso activo WhatsApp TEST');\nconst sesion = base.sesion_activa || actual.sesion_activa || {};\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst normalizarDestino = (valor) => {\n let raw = limpiar(valor);\n\n if (!raw) return '';\n\n raw = raw.replace('@c.us', '@s.whatsapp.net');\n\n if (raw.includes('@g.us')) return raw;\n\n if (raw.includes('@s.whatsapp.net')) {\n const numero = raw.replace('@s.whatsapp.net', '').replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n }\n\n const numero = raw.replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n};\n\nconst idioma = limpiar(\n actual.idioma_flujo ||\n base.idioma_flujo ||\n sesion.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst destinoBase =\n actual.whatsapp_to ||\n actual.group_jid ||\n actual.whatsapp_remote_jid ||\n base.whatsapp_to ||\n base.group_jid ||\n base.whatsapp_remote_jid ||\n sesion.group_jid ||\n sesion.whatsapp_to ||\n sesion.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n actual.manager_telefono ||\n base.sender_phone ||\n actual.sender_phone ||\n '';\n\nif (!destinoBase) {\n throw new Error('No se encontró destino WhatsApp para enviar confirmación de video.');\n}\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '✅ Video received.',\n '',\n 'You can send more videos if needed.',\n '',\n '⚠️ Remember: send each video in a separate message.',\n 'Do not send multiple videos together.',\n '',\n 'When you finish sending videos, write: DONE',\n '',\n 'If you do not have more videos, you can also write: DONE'\n ].join('\\n');\n} else {\n mensaje = [\n '✅ Video recibido.',\n '',\n 'Puedes enviar más videos si hace falta.',\n '',\n '⚠️ Recuerda: envía cada video en un mensaje separado.',\n 'No envíes varios videos juntos.',\n '',\n 'Cuando termines de enviar los videos, escribe: LISTO',\n '',\n 'Si no tienes más videos, también puedes escribir: LISTO'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...base,\n ...actual,\n\n whatsapp_to: normalizarDestino(destinoBase),\n whatsapp_text: mensaje\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -4688, 384 ], "id": "ce363661-867f-4c31-b730-a7aaf88453cc", "name": "Code - Preparar confirmación video WhatsApp TEST" }, { "parameters": { "jsCode": "const base = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\nconst sesion = base.sesion_activa || {};\n\nconst ahora = new Date().toISOString();\n\nconst telefonoBase = String(\n base.whatsapp_to ||\n base.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n ''\n).trim();\n\nconst whatsappTo = telefonoBase.includes('@s.whatsapp.net')\n ? telefonoBase\n : `${telefonoBase.replace(/\\D/g, '')}@s.whatsapp.net`;\n\nreturn [\n {\n json: {\n ...base,\n\n session_id: base.session_id || sesion.session_id || '',\n manager_telefono: sesion.manager_telefono || base.manager_telefono || '',\n manager_nombre: sesion.manager_nombre || base.manager_nombre || '',\n canal_origen: sesion.canal_origen || base.canal_origen || 'WHATSAPP',\n\n fecha_inicio: sesion.fecha_inicio || '',\n ultima_actividad: ahora,\n\n etapa: 'PROCESANDO',\n estado: 'LISTO_PARA_RECUPERAR_MEDIA',\n\n audio_count: Number(sesion.audio_count || 0),\n imagenes_count: Number(sesion.imagenes_count || 0),\n videos_count: Number(sesion.videos_count || 0),\n\n ejecucion_id: sesion.ejecucion_id || base.ejecucion_id || base.session_id || '',\n motivo_revision: 'CON_VIDEO_REPORTADO',\n\n whatsapp_to: whatsappTo\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -6512, 800 ], "id": "b2d8579c-f7b8-486a-a502-3b0e91912628", "name": "Code - Preparar cierre con videos WhatsApp TEST" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ultima_actividad": "={{ $json.ultima_actividad }}", "etapa": "={{ $json.etapa }}", "audio_count": "={{ $json.audio_count }}", "imagenes_count": "={{ $json.imagenes_count }}", "videos_count": "={{ $json.videos_count }}", "estado": "={{ $json.estado }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "motivo_revision": "={{ $json.motivo_revision }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -6304, 800 ], "id": "ccb46bd5-6e01-42d5-89ab-54e4622265be", "name": "Sheets - Actualizar sesión cierre con videos WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const data = $json || {};\nconst base = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\nconst sesion = base.sesion_activa || {};\n\nconst telefonoBase = String(\n base.whatsapp_to ||\n base.whatsapp_remote_jid ||\n data.whatsapp_to ||\n data.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n data.manager_telefono ||\n base.manager_telefono ||\n ''\n).trim();\n\nconst whatsappTo = telefonoBase.includes('@s.whatsapp.net')\n ? telefonoBase\n : `${telefonoBase.replace(/\\D/g, '')}@s.whatsapp.net`;\n\nreturn [\n {\n json: {\n ...base,\n ...data,\n\n whatsapp_to: whatsappTo,\n\n whatsapp_text: `⏳ Tu reporte ya fue recibido y está pendiente de procesamiento.\n\nPor favor espera mientras se prepara el análisis de la evidencia.`\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -4944, 1760 ], "id": "b5960269-5fca-4d10-b2ef-eda72eb68b10", "name": "Code - Preparar aviso procesando WhatsApp TEST" }, { "parameters": { "jsCode": "const data = $json || {};\n\nreturn [\n {\n json: {\n ...data,\n\n session_id_busqueda: data.session_id || '',\n estado_procesamiento: 'BUSCAR_EVENTOS_MEDIA'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -4720, 928 ], "id": "6cee8abe-abb5-4818-ac8c-1f49f0905bcc", "name": "Code - Preparar búsqueda eventos media WhatsApp TEST" }, { "parameters": { "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 306129743, "mode": "list", "cachedResultName": "wa_ejecuciones_eventos", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=306129743" }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -4464, 928 ], "id": "e49fc551-3ddb-4041-88fd-c4329ef64d54", "name": "Sheets - Leer eventos WhatsApp TEST", "alwaysOutputData": true, "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const cierre = $('Code - Preparar búsqueda eventos media WhatsApp TEST').first().json || {};\nconst sessionId = String(cierre.session_id_busqueda || cierre.session_id || '').trim();\n\nconst eventos = $input.all().map(item => item.json || {});\n\nconst eventosSesion = eventos.filter(evento => {\n return String(evento.session_id || '').trim() === sessionId;\n});\n\nconst eventosMedia = eventosSesion.filter(evento => {\n const tipo = String(evento.message_type || '').toLowerCase();\n return ['audio', 'image', 'video'].includes(tipo);\n});\n\nconst audios = eventosMedia.filter(e => String(e.message_type || '').toLowerCase() === 'audio');\nconst imagenes = eventosMedia.filter(e => String(e.message_type || '').toLowerCase() === 'image');\nconst videos = eventosMedia.filter(e => String(e.message_type || '').toLowerCase() === 'video');\n\nreturn [\n {\n json: {\n ...cierre,\n\n total_eventos_sesion: eventosSesion.length,\n total_media: eventosMedia.length,\n\n audio_count_real: audios.length,\n imagenes_count_real: imagenes.length,\n videos_count_real: videos.length,\n\n eventos_audio: audios,\n eventos_imagenes: imagenes,\n eventos_videos: videos,\n\n media_lista: eventosMedia,\n\n estado_procesamiento: 'EVENTOS_MEDIA_FILTRADOS'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -4224, 928 ], "id": "6c7e217e-6200-46d8-892a-adc9b273e4a7", "name": "Code - Filtrar eventos media de sesión WhatsApp TEST" }, { "parameters": { "jsCode": "const data = $json || {};\nconst mediaLista = Array.isArray(data.media_lista) ? data.media_lista : [];\n\nconst sessionId = String(data.session_id || data.session_id_busqueda || '').trim();\nconst ejecucionId = String(data.ejecucion_id || sessionId).trim();\n\nconst items = mediaLista.map((media, index) => {\n const tipo = String(media.message_type || '').toLowerCase();\n const mime = String(media.media_mime_type || '').trim();\n\n let extension = 'bin';\n\n if (mime.includes('ogg')) extension = 'ogg';\n else if (mime.includes('mpeg')) extension = 'mp3';\n else if (mime.includes('mp4')) extension = 'mp4';\n else if (mime.includes('jpeg') || mime.includes('jpg')) extension = 'jpg';\n else if (mime.includes('png')) extension = 'png';\n else if (tipo === 'audio') extension = 'ogg';\n else if (tipo === 'image') extension = 'jpg';\n else if (tipo === 'video') extension = 'mp4';\n\n const mediaSourceId = String(\n media.media_source_id ||\n media.event_id ||\n ''\n ).trim();\n\n const remoteJid = String(\n media.whatsapp_remote_jid ||\n data.whatsapp_remote_jid ||\n data.whatsapp_to ||\n media.manager_telefono ||\n data.manager_telefono ||\n ''\n ).trim();\n\n return {\n json: {\n ...data,\n\n media_index: index + 1,\n media_total: mediaLista.length,\n\n session_id: sessionId,\n ejecucion_id: ejecucionId,\n\n media_event_id: media.event_id || '',\n media_source_id: mediaSourceId,\n media_type: tipo,\n media_mime_type: mime,\n media_extension: extension,\n\n whatsapp_remote_jid: remoteJid,\n\n media_file_name: `${ejecucionId}_${tipo}_${index + 1}.${extension}`,\n\n evento_original: media,\n\n estado_procesamiento: 'MEDIA_ITEM_PREPARADO'\n }\n };\n});\n\nreturn items;" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -3984, 928 ], "id": "6d74dd89-9530-44d7-ae69-2e771bf8aab9", "name": "Code - Separar media en items WhatsApp TEST" }, { "parameters": { "method": "POST", "url": "https://wsp.gomezleemarketing.com/chat/getBase64FromMediaMessage/botsoporte", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "apikey", "value": "={{'C267126ABB45-4C12-B626-6BAB1833F5D7'}}" }, { "name": "Content-Type", "value": "application/json" } ] }, "sendBody": true, "specifyBody": "json", "jsonBody": "={\n \"message\": {\n \"key\": {\n \"id\": \"{{ $json.media_source_id }}\"\n }\n },\n \"convertToMp4\": true\n}", "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ -3776, 928 ], "id": "6c936089-6193-479f-b87b-a98e69e4fa92", "name": "HTTP Request - Obtener media base64 Evolution TEST", "alwaysOutputData": true }, { "parameters": { "jsCode": "const httpItems = $input.all();\nconst mediaItems = $('Code - Separar media en items WhatsApp TEST').all();\n\nconst buscarBase64 = (obj) => {\n if (!obj || typeof obj !== 'object') return '';\n\n if (typeof obj.base64 === 'string') return obj.base64;\n if (typeof obj.data === 'string') return obj.data;\n if (typeof obj.media === 'string') return obj.media;\n\n if (obj.message && typeof obj.message.base64 === 'string') return obj.message.base64;\n if (obj.response && typeof obj.response.base64 === 'string') return obj.response.base64;\n if (obj.data && typeof obj.data.base64 === 'string') return obj.data.base64;\n\n return '';\n};\n\nreturn httpItems.map((item, index) => {\n const respuesta = item.json || {};\n const mediaItem = mediaItems[index]?.json || mediaItems[0]?.json || {};\n\n let base64 = buscarBase64(respuesta);\n\n base64 = String(base64 || '').trim();\n base64 = base64.replace(/^data:.*?;base64,/, '');\n\n if (!base64) {\n throw new Error(`No se encontró base64 para media_source_id: ${mediaItem.media_source_id || 'SIN_ID'}`);\n }\n\n return {\n json: {\n ...mediaItem,\n\n base64_length: base64.length,\n estado_procesamiento: 'MEDIA_BASE64_RECUPERADA'\n },\n binary: {\n data: {\n data: base64,\n mimeType: mediaItem.media_mime_type || 'application/octet-stream',\n fileName: mediaItem.media_file_name || 'media_whatsapp.bin'\n }\n }\n };\n});" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -3568, 928 ], "id": "73f2d7f0-bc17-47cc-9b7a-f477f942bf63", "name": "Code - Convertir base64 a binario WhatsApp TEST" }, { "parameters": { "name": "={{ $json.media_file_name }}", "driveId": { "__rl": true, "mode": "list", "value": "My Drive" }, "folderId": { "__rl": true, "value": "=1G11WZnOwPp7UC2moPE6rEvlPFl7_OoSB", "mode": "id" }, "options": {} }, "type": "n8n-nodes-base.googleDrive", "typeVersion": 3, "position": [ -3328, 928 ], "id": "c9d49cee-9f7f-4dfe-bf4a-5729a3f64298", "name": "Drive - Subir media WhatsApp TEST", "credentials": { "googleDriveOAuth2Api": { "id": "g23xdGLZRzBGqKgH", "name": "Isaac - Google Drive" } } }, { "parameters": { "jsCode": "const driveItems = $input.all();\nconst mediaItems = $('Code - Convertir base64 a binario WhatsApp TEST').all();\n\nreturn driveItems.map((item, index) => {\n const drive = item.json || {};\n const media = mediaItems[index]?.json || mediaItems[0]?.json || {};\n\n const fileId =\n drive.id ||\n drive.fileId ||\n drive.file_id ||\n '';\n\n const webViewLink =\n drive.webViewLink ||\n drive.webContentLink ||\n (fileId ? `https://drive.google.com/file/d/${fileId}/view` : '');\n\n return {\n json: {\n ...media,\n\n drive_file_id: fileId,\n drive_file_name: media.media_file_name,\n drive_file_url: webViewLink,\n\n estado_procesamiento: 'MEDIA_GUARDADA_EN_DRIVE'\n }\n };\n});" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -3120, 928 ], "id": "4bfe263f-272f-4738-8664-4f60990ad359", "name": "Code - Preparar registro media Drive WhatsApp TEST" }, { "parameters": { "jsCode": "const mediaItems = $('Code - Preparar registro media Drive WhatsApp TEST').all();\n\nconst media = mediaItems.map(item => item.json || {});\n\nconst base = media[0] || {};\n\nconst audios = media.filter(m => String(m.media_type || '').toLowerCase() === 'audio');\nconst imagenes = media.filter(m => String(m.media_type || '').toLowerCase() === 'image');\nconst videos = media.filter(m => String(m.media_type || '').toLowerCase() === 'video');\n\nreturn [\n {\n json: {\n ...base,\n\n session_id: base.session_id || '',\n ejecucion_id: base.ejecucion_id || base.session_id || '',\n\n audio_drive_urls: audios.map(m => m.drive_file_url).filter(Boolean).join('\\n'),\n imagenes_drive_urls: imagenes.map(m => m.drive_file_url).filter(Boolean).join('\\n'),\n videos_drive_urls: videos.map(m => m.drive_file_url).filter(Boolean).join('\\n'),\n\n total_media_drive: media.length,\n total_audio_drive: audios.length,\n total_imagenes_drive: imagenes.length,\n total_videos_drive: videos.length,\n\n ultima_actividad: new Date().toISOString(),\n\n etapa: 'PROCESANDO',\n estado: 'LISTO_PARA_ANALIZAR',\n motivo_revision: 'MEDIA_RECUPERADA',\n\n estado_procesamiento: 'MEDIA_RECUPERADA_Y_REGISTRADA'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -2704, 928 ], "id": "8ecc5a7e-5520-49e8-909c-1a59a47f28f9", "name": "Code - Consolidar media recuperada WhatsApp TEST" }, { "parameters": { "operation": "append", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 1962428033, "mode": "list", "cachedResultName": "wa_ejecuciones_media", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=1962428033" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "media_event_id": "={{ $json.media_event_id }}", "media_type": "={{ $json.media_type }}", "media_mime_type": "={{ $json.media_mime_type }}", "media_file_name": "={{ $json.drive_file_name }}", "drive_file_id": "={{ $json.drive_file_id }}", "drive_file_url": "={{ $json.drive_file_url }}", "fecha_guardado": "={{ new Date().toISOString() }}", "estado": "=MEDIA_GUARDADA_EN_DRIVE", "media_index": "={{ $json.media_index }}", "media_total": "={{ $json.media_total }}", "whatsapp_remote_jid": "={{ $json.whatsapp_remote_jid }}" }, "matchingColumns": [], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_event_id", "displayName": "media_event_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_type", "displayName": "media_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_mime_type", "displayName": "media_mime_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_file_name", "displayName": "media_file_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "drive_file_id", "displayName": "drive_file_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "drive_file_url", "displayName": "drive_file_url", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_guardado", "displayName": "fecha_guardado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_index", "displayName": "media_index", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_total", "displayName": "media_total", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "whatsapp_remote_jid", "displayName": "whatsapp_remote_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -2912, 928 ], "id": "900504c8-6e4d-4ce4-bb36-55739dae8408", "name": "Sheets - Guardar media Drive WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "motivo_revision": "={{ $json.motivo_revision }}", "estado": "={{ $json.estado }}", "etapa": "={{ $json.etapa }}", "ultima_actividad": "={{ $json.ultima_actividad }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -2496, 928 ], "id": "6f48b6b3-21bb-4f03-af72-68f67c2e0574", "name": "Sheets - Actualizar sesión media recuperada WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const data = $('Code - Consolidar media recuperada WhatsApp TEST').first().json || {};\n\nconst audioUrls = String(data.audio_drive_urls || '').trim();\nconst imagenesUrls = String(data.imagenes_drive_urls || '').trim();\nconst videosUrls = String(data.videos_drive_urls || '').trim();\n\nconst prompt = `\nAnaliza esta ejecución de propuesta usando la evidencia disponible.\n\nIMPORTANTE:\n- La nota de voz contiene la descripción principal de la ejecución.\n- Las imágenes muestran evidencia visual obligatoria.\n- Los videos son evidencia opcional adicional.\n- No inventes datos que no estén claros.\n- Si un dato no aparece, marca \"No identificado\".\n\nDebes devolver SOLO un JSON válido con esta estructura:\n\n{\n \"propuesta_referencia\": \"\",\n \"marca\": \"\",\n \"cliente\": \"\",\n \"pais\": \"\",\n \"ubicacion\": \"\",\n \"fecha_ejecucion\": \"\",\n \"que_se_implemento\": \"\",\n \"comentarios_resultados\": \"\",\n \"resumen_ejecucion\": \"\",\n \"nivel_confianza\": \"\",\n \"requiere_revision\": \"\",\n \"motivo_revision\": \"\"\n}\n\nDatos de control:\nsession_id: ${data.session_id}\nejecucion_id: ${data.ejecucion_id}\nmanager_nombre: ${data.manager_nombre}\nmanager_telefono: ${data.manager_telefono}\n\nArchivos de audio en Drive:\n${audioUrls || 'No hay audio registrado'}\n\nImágenes en Drive:\n${imagenesUrls || 'No hay imágenes registradas'}\n\nVideos en Drive:\n${videosUrls || 'No hay videos registrados'}\n`.trim();\n\nreturn [\n {\n json: {\n ...data,\n gemini_prompt: prompt,\n estado_procesamiento: 'PAQUETE_GEMINI_PREPARADO'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -2288, 928 ], "id": "6fefc255-e85b-4028-b9e9-b3f71abe1457", "name": "Code - Preparar paquete análisis Gemini WhatsApp TEST" }, { "parameters": { "rules": { "values": [ { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "leftValue": "={{ String($json.media_type || '').toLowerCase() }}", "rightValue": "audio", "operator": { "type": "string", "operation": "equals" }, "id": "ddf19bdc-2a61-4261-8950-02ea3026ba01" } ], "combinator": "and" }, "renameOutput": true, "outputKey": "audio" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "dcf1d733-7b80-4f67-b6f6-9e965a5bae24", "leftValue": "={{ String($json.media_type || '').toLowerCase() }}", "rightValue": "image", "operator": { "type": "string", "operation": "equals" } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "=image" }, { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "bb7e48e1-8383-463c-a43e-7e8e5d1ae21d", "leftValue": "={{ String($json.media_type || '').toLowerCase() }}", "rightValue": "video", "operator": { "type": "string", "operation": "equals", "name": "filter.operator.equals" } } ], "combinator": "and" }, "renameOutput": true, "outputKey": "VIDEO" } ] }, "options": {} }, "type": "n8n-nodes-base.switch", "typeVersion": 3.4, "position": [ -3248, 1376 ], "id": "235d1178-5ecf-4225-b1c5-792512a22877", "name": "Switch - Tipo media para Gemini WhatsApp TEST" }, { "parameters": { "jsCode": "const input = $input.first();\nconst data = input.json || {};\nconst binary = input.binary || {};\n\nif (!binary.data) {\n throw new Error('No llegó binary.data al nodo de audio para Gemini');\n}\n\nconst geminiPromptAudio = `\nAnaliza esta nota de voz enviada por WhatsApp para el flujo de registro de propuestas de Fulgencio Fumado.\n\nObjetivo:\nExtraer información útil del reporte y clasificar correctamente el tipo de reporte.\n\nPrimero, identifica el tipo de reporte usando exactamente uno de estos valores:\n\n1. PROPUESTA_EJECUTADA\nCuando el manager reporta evidencia de una propuesta que ya fue implementada, instalada, ejecutada, realizada o activada.\n\n2. PROPUESTA_EXTERNA\nCuando el manager reporta una propuesta, actividad, idea, ejecución, referencia o material recibido fuera del banco interno de propuestas y que debe documentarse.\n\n3. NO_DETERMINADO\nCuando la nota de voz no permite saber claramente si es una propuesta ejecutada o una propuesta externa.\n\nReglas para clasificar:\n- Si el manager dice explícitamente \"propuesta ejecutada\", clasifica como PROPUESTA_EJECUTADA.\n- Si el manager dice explícitamente \"ejecutada\", \"ya se ejecutó\", \"se implementó\", \"se realizó\", \"se instaló\" o expresiones equivalentes, clasifica como PROPUESTA_EJECUTADA.\n- Si el manager dice explícitamente \"propuesta externa\", clasifica como PROPUESTA_EXTERNA.\n- Si el manager dice \"externa\", \"fuera del banco\", \"recibida fuera del banco interno\", \"referencia externa\" o expresiones equivalentes, clasifica como PROPUESTA_EXTERNA.\n- Si solo hay una prueba de audio, saludo, ruido, conteo, información incompleta o no se entiende el objetivo, clasifica como NO_DETERMINADO.\n- No inventes el tipo de reporte.\n- Si hay duda, usa NO_DETERMINADO.\n\nAdemás, extrae la mayor cantidad posible de información útil:\n- Nombre o referencia de la propuesta\n- Marca o cliente\n- País\n- Ubicación\n- Fecha de ejecución o fecha del reporte\n- Qué se implementó o qué se está reportando\n- Comentarios o resultados observados\n- Si el manager menciona que es ejecutada, externa o no queda claro\n\nDevuelve el análisis en texto claro y estructurado, incluyendo obligatoriamente estas líneas:\n\nTipo de reporte detectado: PROPUESTA_EJECUTADA | PROPUESTA_EXTERNA | NO_DETERMINADO\nConfianza del tipo de reporte: ALTA | MEDIA | BAJA\nMotivo del tipo de reporte: explicación breve\n\nNo inventes datos. Si algo no está claro, indica \"No identificado\".\n`.trim();\n\nreturn [\n {\n json: {\n ...data,\n gemini_prompt_audio: geminiPromptAudio\n },\n binary\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -2768, 1200 ], "id": "e3cfe03a-110b-4470-9011-e4705dcde4a7", "name": "Code - Preparar Gemini audio WhatsApp TEST" }, { "parameters": { "resource": "audio", "operation": "analyze", "modelId": { "__rl": true, "value": "models/gemini-3.1-flash-lite", "mode": "list", "cachedResultName": "models/gemini-3.1-flash-lite" }, "text": "={{ $json.gemini_prompt_audio }}", "inputType": "binary", "options": {} }, "type": "@n8n/n8n-nodes-langchain.googleGemini", "typeVersion": 1.2, "position": [ -2560, 1200 ], "id": "18ddf06b-efd7-49b0-b4bf-0f68e4c91a20", "name": "Gemini - Analizar audio WhatsApp TEST", "credentials": { "googlePalmApi": { "id": "6Jdcd9FONxG35YuJ", "name": "Isaac - Gemini Api Gratis" } } }, { "parameters": { "jsCode": "const resultados = $input.all();\nconst fuentes = $('Code - Preparar Gemini audio WhatsApp TEST').all();\n\nconst extraerTexto = (obj) => {\n if (!obj || typeof obj !== 'object') return '';\n\n if (typeof obj.text === 'string') return obj.text;\n if (typeof obj.output === 'string') return obj.output;\n if (typeof obj.response === 'string') return obj.response;\n if (typeof obj.content === 'string') return obj.content;\n\n if (Array.isArray(obj.candidates)) {\n return JSON.stringify(obj.candidates);\n }\n\n return JSON.stringify(obj);\n};\n\nreturn resultados.map((item, index) => {\n const gemini = item.json || {};\n const media = fuentes[index]?.json || fuentes[0]?.json || {};\n\n return {\n json: {\n session_id: media.session_id || '',\n ejecucion_id: media.ejecucion_id || '',\n media_event_id: media.media_event_id || '',\n media_type: 'audio',\n media_file_name: media.media_file_name || '',\n analisis_texto: extraerTexto(gemini),\n fecha_analisis: new Date().toISOString(),\n estado: 'ANALISIS_AUDIO_COMPLETADO',\n media_index: media.media_index || '',\n media_total: media.media_total || ''\n }\n };\n});" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -2352, 1200 ], "id": "2587e982-2403-450c-9419-a9de41d3c5a1", "name": "Code - Normalizar análisis audio WhatsApp TEST" }, { "parameters": { "jsCode": "const input = $input.first();\nconst data = input.json || {};\nconst binary = input.binary || {};\n\nif (!binary.data) {\n throw new Error('No llegó binary.data al nodo de imagen para Gemini');\n}\n\nreturn [\n {\n json: {\n ...data,\n gemini_prompt_imagen: `Analiza esta imagen como evidencia de una propuesta ejecutada.\n\nDescribe:\n- Qué se observa en la imagen\n- Elementos de marca visibles\n- Materiales instalados o implementados\n- Posible ubicación visible\n- Calidad de la implementación\n- Detalles relevantes para documentar la ejecución\n\nNo inventes datos que no sean visibles.`\n },\n binary\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -2752, 1424 ], "id": "68e8b0de-373c-48fa-a762-8c0e45d64c15", "name": "Code - Preparar Gemini imagen WhatsApp TEST" }, { "parameters": { "resource": "image", "operation": "analyze", "modelId": { "__rl": true, "value": "models/gemini-3.1-flash-lite", "mode": "list", "cachedResultName": "models/gemini-3.1-flash-lite" }, "text": "={{ $json.gemini_prompt_imagen }}", "inputType": "binary", "options": {} }, "type": "@n8n/n8n-nodes-langchain.googleGemini", "typeVersion": 1.2, "position": [ -2544, 1424 ], "id": "1baf8c8e-95cf-42fe-af4f-fcbc71a557fc", "name": "Gemini - Analizar imagen WhatsApp TEST", "credentials": { "googlePalmApi": { "id": "6Jdcd9FONxG35YuJ", "name": "Isaac - Gemini Api Gratis" } } }, { "parameters": { "jsCode": "const resultados = $input.all();\nconst fuentes = $('Code - Preparar Gemini imagen WhatsApp TEST').all();\n\nconst extraerTexto = (obj) => {\n if (!obj || typeof obj !== 'object') return '';\n\n if (typeof obj.text === 'string') return obj.text;\n if (typeof obj.output === 'string') return obj.output;\n if (typeof obj.response === 'string') return obj.response;\n if (typeof obj.content === 'string') return obj.content;\n\n if (Array.isArray(obj.candidates)) {\n return JSON.stringify(obj.candidates);\n }\n\n return JSON.stringify(obj);\n};\n\nreturn resultados.map((item, index) => {\n const gemini = item.json || {};\n const media = fuentes[index]?.json || fuentes[0]?.json || {};\n\n return {\n json: {\n session_id: media.session_id || '',\n ejecucion_id: media.ejecucion_id || '',\n media_event_id: media.media_event_id || '',\n media_type: 'image',\n media_file_name: media.media_file_name || '',\n analisis_texto: extraerTexto(gemini),\n fecha_analisis: new Date().toISOString(),\n estado: 'ANALISIS_IMAGEN_COMPLETADO',\n media_index: media.media_index || '',\n media_total: media.media_total || ''\n }\n };\n});" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -2336, 1424 ], "id": "e1bc474b-82b0-4a4c-9ef7-e5f52b2b7503", "name": "Code - Normalizar análisis imagen WhatsApp TEST" }, { "parameters": { "jsCode": "const input = $input.first();\nconst data = input.json || {};\nconst binary = input.binary || {};\n\nif (!binary.data) {\n throw new Error('No llegó binary.data al nodo de video para Gemini');\n}\n\nreturn [\n {\n json: {\n ...data,\n gemini_prompt_video: `Analiza este video como evidencia de una propuesta ejecutada.\n\nDescribe:\n- Qué se observa en el video\n- Elementos de marca visibles\n- Materiales instalados o implementados\n- Interacciones o movimientos relevantes\n- Posible ubicación visible\n- Calidad de la implementación\n- Resultados observables\n\nNo inventes datos que no sean visibles.`\n },\n binary\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -2880, 1664 ], "id": "5bfc997b-a538-4d96-8d7d-e4abe8e4cf9f", "name": "Code - Preparar Gemini video WhatsApp TEST" }, { "parameters": { "resource": "video", "operation": "analyze", "modelId": { "__rl": true, "value": "models/gemini-3.1-flash-lite", "mode": "list", "cachedResultName": "models/gemini-3.1-flash-lite" }, "text": "={{ $json.gemini_prompt_video }}", "inputType": "binary", "options": {} }, "type": "@n8n/n8n-nodes-langchain.googleGemini", "typeVersion": 1.2, "position": [ -2672, 1664 ], "id": "5767efd8-bfb6-4221-964d-743b417be1aa", "name": "Analyze video", "credentials": { "googlePalmApi": { "id": "6Jdcd9FONxG35YuJ", "name": "Isaac - Gemini Api Gratis" } } }, { "parameters": { "jsCode": "const resultados = $input.all();\nconst fuentes = $('Code - Preparar Gemini video WhatsApp TEST').all();\n\nconst extraerTexto = (obj) => {\n if (!obj || typeof obj !== 'object') return '';\n\n if (typeof obj.text === 'string') return obj.text;\n if (typeof obj.output === 'string') return obj.output;\n if (typeof obj.response === 'string') return obj.response;\n if (typeof obj.content === 'string') return obj.content;\n\n if (Array.isArray(obj.candidates)) {\n return JSON.stringify(obj.candidates);\n }\n\n return JSON.stringify(obj);\n};\n\nreturn resultados.map((item, index) => {\n const gemini = item.json || {};\n const media = fuentes[index]?.json || fuentes[0]?.json || {};\n\n return {\n json: {\n session_id: media.session_id || '',\n ejecucion_id: media.ejecucion_id || '',\n media_event_id: media.media_event_id || '',\n media_type: 'video',\n media_file_name: media.media_file_name || '',\n analisis_texto: extraerTexto(gemini),\n fecha_analisis: new Date().toISOString(),\n estado: 'ANALISIS_VIDEO_COMPLETADO',\n media_index: media.media_index || '',\n media_total: media.media_total || ''\n }\n };\n});" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -2464, 1664 ], "id": "c035db53-87b9-4fc2-9346-d5228bbac7b7", "name": "Code - Normalizar análisis video WhatsApp TEST" }, { "parameters": { "operation": "append", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 1107394537, "mode": "list", "cachedResultName": "wa_ejecuciones_analisis_media", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=1107394537" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "media_event_id": "={{ $json.media_event_id }}", "media_type": "={{ $json.media_type }}", "media_file_name": "={{ $json.media_file_name }}", "analisis_texto": "={{ $json.analisis_texto }}", "fecha_analisis": "={{ $json.fecha_analisis }}", "estado": "={{ $json.estado }}", "media_index": "={{ $json.media_index }}", "media_total": "={{ $json.media_total }}" }, "matchingColumns": [], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_event_id", "displayName": "media_event_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_type", "displayName": "media_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_file_name", "displayName": "media_file_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "analisis_texto", "displayName": "analisis_texto", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_analisis", "displayName": "fecha_analisis", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_index", "displayName": "media_index", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_total", "displayName": "media_total", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -1872, 1376 ], "id": "55f75826-cb50-4857-b58f-11bce7703b47", "name": "Sheets - Guardar análisis media Gemini WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 1107394537, "mode": "list", "cachedResultName": "wa_ejecuciones_analisis_media", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=1107394537" }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -1184, 1360 ], "id": "d68f5e66-a1cb-48cc-afbd-07677f3145cb", "name": "Sheets - Leer análisis media Gemini WhatsApp TEST", "alwaysOutputData": true, "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const cierre = $('Code - Consolidar media recuperada WhatsApp TEST').first().json || {};\nconst evento = $('Code - Resolver paso activo WhatsApp TEST').first().json || {};\n\nconst sessionId = String(cierre.session_id || evento.session_id || '').trim();\nconst ejecucionId = String(cierre.ejecucion_id || evento.ejecucion_id || sessionId).trim();\n\nconst rows = $input.all().map(item => item.json || {});\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst extraerTextoAnalisis = (valor) => {\n if (!valor) return '';\n\n if (typeof valor === 'object') {\n return extraerDesdeObjeto(valor);\n }\n\n const texto = String(valor || '').trim();\n\n if (!texto) return '';\n\n try {\n const obj = JSON.parse(texto);\n const extraido = extraerDesdeObjeto(obj);\n if (extraido) return extraido;\n } catch (error) {}\n\n return texto;\n};\n\nconst extraerDesdeObjeto = (obj) => {\n if (!obj || typeof obj !== 'object') return '';\n\n if (typeof obj.text === 'string') return obj.text.trim();\n if (typeof obj.output === 'string') return obj.output.trim();\n if (typeof obj.response === 'string') return obj.response.trim();\n if (typeof obj.content === 'string') return obj.content.trim();\n\n if (Array.isArray(obj.content?.parts)) {\n const txt = obj.content.parts.map(p => p.text || '').join('\\n').trim();\n if (txt) return txt;\n }\n\n if (Array.isArray(obj.parts)) {\n const txt = obj.parts.map(p => p.text || '').join('\\n').trim();\n if (txt) return txt;\n }\n\n if (Array.isArray(obj.candidates)) {\n const txt = obj.candidates\n .flatMap(c => c.content?.parts || [])\n .map(p => p.text || '')\n .join('\\n')\n .trim();\n\n if (txt) return txt;\n }\n\n if (obj.message && typeof obj.message === 'object') {\n const txt = extraerDesdeObjeto(obj.message);\n if (txt) return txt;\n }\n\n if (obj.data && typeof obj.data === 'object') {\n const txt = extraerDesdeObjeto(obj.data);\n if (txt) return txt;\n }\n\n return '';\n};\n\nconst analisisSesion = rows.filter(row => {\n return String(row.session_id || '').trim() === sessionId;\n});\n\n// Evitar duplicados por pruebas manuales\nconst vistos = new Set();\nconst analisisUnicos = [];\n\nfor (const row of analisisSesion) {\n const key = `${row.media_event_id || ''}_${row.media_type || ''}`;\n\n if (vistos.has(key)) continue;\n\n vistos.add(key);\n\n analisisUnicos.push({\n ...row,\n analisis_texto_limpio: extraerTextoAnalisis(row.analisis_texto)\n });\n}\n\nconst audios = analisisUnicos.filter(r => String(r.media_type || '').toLowerCase() === 'audio');\nconst imagenes = analisisUnicos.filter(r => String(r.media_type || '').toLowerCase() === 'image');\nconst videos = analisisUnicos.filter(r => String(r.media_type || '').toLowerCase() === 'video');\n\nconst textoAudio = audios.map(r => r.analisis_texto_limpio).filter(Boolean).join('\\n\\n---\\n\\n');\nconst textoImagenes = imagenes.map(r => r.analisis_texto_limpio).filter(Boolean).join('\\n\\n---\\n\\n');\nconst textoVideos = videos.map(r => r.analisis_texto_limpio).filter(Boolean).join('\\n\\n---\\n\\n');\n\nconst textoTotal = [textoAudio, textoImagenes, textoVideos].filter(Boolean).join('\\n\\n');\n\nif (!textoTotal) {\n throw new Error(`No hay texto limpio de análisis para la sesión ${sessionId}. Revisa wa_ejecuciones_analisis_media.`);\n}\n\nconst managerNombre =\n limpiar(cierre.manager_nombre) ||\n limpiar(evento.manager_nombre) ||\n 'No identificado';\n\nconst managerTelefono =\n limpiar(cierre.manager_telefono) ||\n limpiar(evento.manager_telefono) ||\n 'No identificado';\n\nconst promptFinal = `\nEres un analista de propuestas de GomezLee Marketing.\n\nCon base en el análisis del audio, imágenes y videos, genera un JSON final estructurado para registrar el reporte.\n\nReglas generales:\n- Devuelve SOLO JSON válido.\n- No uses markdown.\n- No inventes información.\n- Si un dato no está claro, usa \"No identificado\".\n- El audio tiene prioridad para datos declarados por el manager.\n- Las imágenes y videos sirven para validar evidencia visual.\n- Si hay dudas importantes, requiere_revision debe ser \"SI\"; si todo está claro, \"NO\".\n- nivel_confianza debe ser \"ALTA\", \"MEDIA\" o \"BAJA\".\n- resumen_ejecucion debe ser una síntesis útil para un reporte ejecutivo.\n- que_se_implemento debe describir claramente lo ejecutado o reportado.\n\nClasifica también el tipo de reporte.\n\ntipo_reporte debe ser exactamente uno de estos valores:\n- PROPUESTA_EJECUTADA\n- PROPUESTA_EXTERNA\n- NO_DETERMINADO\n\ntipo_reporte_confianza debe ser exactamente uno de estos valores:\n- ALTA\n- MEDIA\n- BAJA\n\ntipo_reporte_motivo debe explicar brevemente por qué se eligió ese tipo.\n\nReglas para tipo_reporte:\n- Si el audio menciona claramente que es una propuesta ejecutada, usa PROPUESTA_EJECUTADA.\n- Si el audio menciona que ya se ejecutó, se implementó, se instaló, se realizó o se activó una propuesta, usa PROPUESTA_EJECUTADA.\n- Si el audio menciona claramente que es una propuesta externa, usa PROPUESTA_EXTERNA.\n- Si el audio menciona algo recibido fuera del banco interno, referencia externa, idea externa, actividad externa o material externo que debe documentarse, usa PROPUESTA_EXTERNA.\n- Si el audio es una prueba, saludo, ruido, información incompleta o no permite clasificar, usa NO_DETERMINADO.\n- No uses las imágenes para inventar el tipo si el audio no lo confirma.\n- El audio tiene prioridad para determinar el tipo de reporte.\n- Si hay duda entre ejecutada y externa, usa NO_DETERMINADO.\n\nEstructura obligatoria:\n\n{\n \"tipo_reporte\": \"\",\n \"tipo_reporte_confianza\": \"\",\n \"tipo_reporte_motivo\": \"\",\n \"propuesta_referencia\": \"\",\n \"marca\": \"\",\n \"cliente\": \"\",\n \"pais\": \"\",\n \"ubicacion\": \"\",\n \"fecha_ejecucion\": \"\",\n \"que_se_implemento\": \"\",\n \"comentarios_resultados\": \"\",\n \"resumen_ejecucion\": \"\",\n \"evidencia_audio_resumen\": \"\",\n \"evidencia_imagenes_resumen\": \"\",\n \"evidencia_videos_resumen\": \"\",\n \"nivel_confianza\": \"\",\n \"requiere_revision\": \"\",\n \"motivo_revision\": \"\"\n}\n\nDatos de control:\nsession_id: ${sessionId}\nejecucion_id: ${ejecucionId}\nmanager_nombre: ${managerNombre}\nmanager_telefono: ${managerTelefono}\n\nANÁLISIS DEL AUDIO:\n${textoAudio || 'No hay análisis de audio disponible.'}\n\nANÁLISIS DE IMÁGENES:\n${textoImagenes || 'No hay análisis de imágenes disponible.'}\n\nANÁLISIS DE VIDEOS:\n${textoVideos || 'No hay análisis de videos disponible.'}\n`.trim();\n\nreturn [\n {\n json: {\n ...cierre,\n\n session_id: sessionId,\n ejecucion_id: ejecucionId,\n\n manager_nombre: managerNombre,\n manager_telefono: managerTelefono,\n canal_origen: cierre.canal_origen || evento.canal_origen || 'WHATSAPP',\n\n total_analisis_media: analisisUnicos.length,\n total_analisis_audio: audios.length,\n total_analisis_imagenes: imagenes.length,\n total_analisis_videos: videos.length,\n\n texto_audio: textoAudio,\n texto_imagenes: textoImagenes,\n texto_videos: textoVideos,\n\n gemini_prompt_final: promptFinal,\n\n estado_procesamiento: 'ANALISIS_MEDIA_CONSOLIDADO'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -976, 1360 ], "id": "2a9d4918-8e52-4633-8369-c4c8397e9129", "name": "Code - Consolidar análisis media Gemini WhatsApp TEST" }, { "parameters": { "modelId": { "__rl": true, "value": "models/gemini-3.1-flash-lite", "mode": "list", "cachedResultName": "models/gemini-3.1-flash-lite" }, "messages": { "values": [ { "content": "={{ $json.gemini_prompt_final }}" } ] }, "builtInTools": {}, "options": {} }, "type": "@n8n/n8n-nodes-langchain.googleGemini", "typeVersion": 1.2, "position": [ -768, 1360 ], "id": "76e59ce7-1844-40e3-bd7d-1e4101838aad", "name": "Gemini - Generar JSON final propuesta ejecutada TEST", "credentials": { "googlePalmApi": { "id": "6Jdcd9FONxG35YuJ", "name": "Isaac - Gemini Api Gratis" } } }, { "parameters": { "jsCode": "const gemini = $json || {};\nconst base = $('Code - Consolidar análisis media Gemini WhatsApp TEST').first().json || {};\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst extraerTexto = (obj) => {\n if (!obj || typeof obj !== 'object') return '';\n\n if (typeof obj.text === 'string') return obj.text;\n if (typeof obj.output === 'string') return obj.output;\n if (typeof obj.response === 'string') return obj.response;\n if (typeof obj.content === 'string') return obj.content;\n\n if (Array.isArray(obj.content?.parts)) {\n const txt = obj.content.parts.map(p => p.text || '').join('\\n').trim();\n if (txt) return txt;\n }\n\n if (Array.isArray(obj.parts)) {\n const txt = obj.parts.map(p => p.text || '').join('\\n').trim();\n if (txt) return txt;\n }\n\n if (Array.isArray(obj.candidates)) {\n const txt = obj.candidates\n .flatMap(c => c.content?.parts || [])\n .map(p => p.text || '')\n .join('\\n')\n .trim();\n\n if (txt) return txt;\n }\n\n if (obj.message && typeof obj.message === 'object') {\n const txt = extraerTexto(obj.message);\n if (txt) return txt;\n }\n\n if (obj.data && typeof obj.data === 'object') {\n const txt = extraerTexto(obj.data);\n if (txt) return txt;\n }\n\n return JSON.stringify(obj);\n};\n\nlet texto = extraerTexto(gemini).trim();\n\ntexto = texto\n .replace(/^```json/i, '')\n .replace(/^```/i, '')\n .replace(/```$/i, '')\n .trim();\n\nlet parsed = {};\n\ntry {\n parsed = JSON.parse(texto);\n} catch (error) {\n parsed = {\n tipo_reporte: 'NO_DETERMINADO',\n tipo_reporte_confianza: 'BAJA',\n tipo_reporte_motivo: `No se pudo parsear el JSON final de Gemini: ${error.message}`,\n\n propuesta_referencia: 'No identificado',\n marca: 'No identificado',\n cliente: 'No identificado',\n pais: 'No identificado',\n ubicacion: 'No identificado',\n fecha_ejecucion: 'No identificado',\n que_se_implemento: 'No identificado',\n comentarios_resultados: '',\n resumen_ejecucion: texto || 'No se pudo parsear el JSON de Gemini.',\n evidencia_audio_resumen: base.texto_audio || '',\n evidencia_imagenes_resumen: base.texto_imagenes || '',\n evidencia_videos_resumen: base.texto_videos || '',\n nivel_confianza: 'BAJA',\n requiere_revision: 'SI',\n motivo_revision: `Error parseando JSON final: ${error.message}`\n };\n}\n\nconst valor = (campo, fallback = 'No identificado') => {\n const v = limpiar(campo);\n if (!v) return fallback;\n if (v.toLowerCase() === 'undefined') return fallback;\n if (v.toLowerCase() === 'null') return fallback;\n return v;\n};\n\nconst normalizarTipoReporte = (valorTipo) => {\n const texto = String(valorTipo ?? '')\n .trim()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toUpperCase()\n .replace(/\\s+/g, '_');\n\n if (\n texto === 'PROPUESTA_EJECUTADA' ||\n texto === 'EJECUTADA' ||\n texto === 'EJECUTADO'\n ) {\n return 'PROPUESTA_EJECUTADA';\n }\n\n if (\n texto === 'PROPUESTA_EXTERNA' ||\n texto === 'EXTERNA' ||\n texto === 'EXTERNO'\n ) {\n return 'PROPUESTA_EXTERNA';\n }\n\n return 'NO_DETERMINADO';\n};\n\nconst normalizarConfianza = (valorConfianza) => {\n const texto = String(valorConfianza ?? '')\n .trim()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toUpperCase();\n\n if (['ALTA', 'MEDIA', 'BAJA'].includes(texto)) {\n return texto;\n }\n\n return 'BAJA';\n};\n\nconst ahora = new Date().toISOString();\n\nconst requiereRevision = valor(parsed.requiere_revision, 'SI').toUpperCase();\n\nconst estadoRevision = requiereRevision === 'SI'\n ? 'PENDIENTE_REVISION'\n : 'ANALISIS_COMPLETADO';\n\nconst evidenciaImagenes = valor(parsed.evidencia_imagenes_resumen, '');\nconst evidenciaVideos = valor(parsed.evidencia_videos_resumen, '');\n\nconst elementosDetectados = [\n evidenciaImagenes,\n evidenciaVideos\n].filter(Boolean).join('\\n\\n');\n\nconst tags = [\n parsed.marca,\n parsed.cliente,\n parsed.pais,\n parsed.ubicacion\n]\n .map(v => valor(v, ''))\n .filter(Boolean)\n .join(', ');\n\nconst tipoReporte = normalizarTipoReporte(parsed.tipo_reporte);\nconst tipoReporteConfianza = normalizarConfianza(parsed.tipo_reporte_confianza);\nconst tipoReporteMotivo = valor(parsed.tipo_reporte_motivo, 'No identificado');\n\nreturn [\n {\n json: {\n ...base,\n\n ejecucion_id: base.ejecucion_id || base.session_id || '',\n session_id: base.session_id || '',\n\n fecha_recepcion: base.fecha_recepcion || ahora,\n fecha_ejecucion: valor(parsed.fecha_ejecucion),\n\n manager_nombre: valor(base.manager_nombre),\n manager_telefono: valor(base.manager_telefono),\n canal_origen: base.canal_origen || 'WHATSAPP',\n\n tipo_reporte: tipoReporte,\n tipo_reporte_confianza: tipoReporteConfianza,\n tipo_reporte_motivo: tipoReporteMotivo,\n\n propuesta_referencia: valor(parsed.propuesta_referencia),\n\n propuesta_match_estado: 'PENDIENTE',\n propuesta_match_confianza: '',\n propuesta_nombre_banco: '',\n propuesta_link_banco: '',\n propuesta_match_revision: 'PENDIENTE_MATCH_BANCO',\n\n marca: valor(parsed.marca),\n cliente: valor(parsed.cliente),\n pais: valor(parsed.pais),\n ubicacion: valor(parsed.ubicacion),\n\n comentario_original: valor(parsed.evidencia_audio_resumen || base.texto_audio, ''),\n resumen_ia: valor(parsed.resumen_ejecucion, ''),\n descripcion_ejecucion: valor(parsed.que_se_implemento),\n elementos_detectados: elementosDetectados,\n tags,\n\n media_folder_url: base.media_folder_url || '',\n presentacion_ejecucion_url: '',\n\n fotos_count: base.total_analisis_imagenes || 0,\n videos_count: base.total_analisis_videos || 0,\n audios_count: base.total_analisis_audio || 0,\n\n estado_revision: estadoRevision,\n motivo_revision: valor(parsed.motivo_revision, ''),\n ultima_actualizacion: ahora,\n\n transcripcion_audio: valor(parsed.evidencia_audio_resumen || base.texto_audio, ''),\n\n nivel_confianza: valor(parsed.nivel_confianza, 'BAJA'),\n requiere_revision: requiereRevision,\n\n gemini_json_raw: texto\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -416, 1360 ], "id": "891ffabf-cb1a-415a-8b29-3facb39f4efa", "name": "Code - Normalizar JSON final propuesta ejecutada TEST" }, { "parameters": { "operation": "append", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 737153956, "mode": "list", "cachedResultName": "propuestas_ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=737153956" }, "columns": { "mappingMode": "defineBelow", "value": { "ejecucion_id": "={{ $json.ejecucion_id }}", "fecha_recepcion": "={{ $json.fecha_recepcion }}", "fecha_ejecucion": "={{ $json.fecha_ejecucion }}", "manager_nombre": "={{ $json.manager_nombre }}", "manager_telefono": "={{ $json.manager_telefono }}", "propuesta_referencia": "={{ $json.propuesta_referencia }}", "propuesta_match_estado": "={{ $json.propuesta_match_estado }}", "propuesta_match_confianza": "={{ $json.propuesta_match_confianza }}", "propuesta_nombre_banco": "={{ $json.propuesta_nombre_banco }}", "propuesta_link_banco": "={{ $json.propuesta_link_banco }}", "marca": "={{ $json.marca }}", "cliente": "={{ $json.cliente }}", "pais": "={{ $json.pais }}", "ubicacion": "={{ $json.ubicacion }}", "comentario_original": "={{ $json.comentario_original }}", "resumen_ia": "={{ $json.resumen_ia }}", "descripcion_ejecucion": "={{ $json.descripcion_ejecucion }}", "elementos_detectados": "={{ $json.elementos_detectados }}", "tags": "={{ $json.tags }}", "media_folder_url": "={{ $json.media_folder_url }}", "presentacion_ejecucion_url": "={{ $json.presentacion_ejecucion_url }}", "fotos_count": "={{ $json.fotos_count }}", "videos_count": "={{ $json.videos_count }}", "audios_count": "={{ $json.audios_count }}", "estado_revision": "={{ $json.estado_revision }}", "motivo_revision": "={{ $json.motivo_revision }}", "ultima_actualizacion": "={{ $json.ultima_actualizacion }}", "session_id": "={{ $json.session_id }}", "canal_origen": "={{ $json.canal_origen }}", "transcripcion_audio": "={{ $json.transcripcion_audio }}", "propuesta_match_revision": "={{ $json.propuesta_match_revision }}", "tipo_reporte": "={{ $json.tipo_reporte }}", "tipo_reporte_confianza": "={{ $json.tipo_reporte_confianza }}", "tipo_reporte_motivo": "={{ $json.tipo_reporte_motivo }}" }, "matchingColumns": [], "schema": [ { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_recepcion", "displayName": "fecha_recepcion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_ejecucion", "displayName": "fecha_ejecucion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_referencia", "displayName": "propuesta_referencia", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_match_estado", "displayName": "propuesta_match_estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_match_confianza", "displayName": "propuesta_match_confianza", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_nombre_banco", "displayName": "propuesta_nombre_banco", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_link_banco", "displayName": "propuesta_link_banco", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "marca", "displayName": "marca", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "cliente", "displayName": "cliente", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "pais", "displayName": "pais", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ubicacion", "displayName": "ubicacion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "comentario_original", "displayName": "comentario_original", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "resumen_ia", "displayName": "resumen_ia", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "descripcion_ejecucion", "displayName": "descripcion_ejecucion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "elementos_detectados", "displayName": "elementos_detectados", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "tags", "displayName": "tags", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_folder_url", "displayName": "media_folder_url", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "presentacion_ejecucion_url", "displayName": "presentacion_ejecucion_url", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fotos_count", "displayName": "fotos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audios_count", "displayName": "audios_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado_revision", "displayName": "estado_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actualizacion", "displayName": "ultima_actualizacion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "transcripcion_audio", "displayName": "transcripcion_audio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_match_revision", "displayName": "propuesta_match_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "tipo_reporte", "displayName": "tipo_reporte", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "tipo_reporte_confianza", "displayName": "tipo_reporte_confianza", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "tipo_reporte_motivo", "displayName": "tipo_reporte_motivo", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ 2144, 1536 ], "id": "705689dd-2988-4aa4-835b-3386b5d24e13", "name": "Sheets - Guardar propuesta ejecutada final TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 616166581, "mode": "list", "cachedResultName": "wa_ejecuciones_sesiones", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=616166581" }, "columns": { "mappingMode": "defineBelow", "value": { "session_id": "={{ $json.session_id }}", "ultima_actividad": "={{ new Date().toISOString() }}", "etapa": "={{ 'COMPLETADO' }}", "estado": "={{ 'ANALISIS_COMPLETADO' }}", "motivo_revision": "={{ $json.motivo_revision || 'ANALISIS_COMPLETADO' }}", "tipo_reporte": "={{ $json.tipo_reporte }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_inicio", "displayName": "fecha_inicio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actividad", "displayName": "ultima_actividad", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "etapa", "displayName": "etapa", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audio_count", "displayName": "audio_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "imagenes_count", "displayName": "imagenes_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado", "displayName": "estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "is_group", "displayName": "is_group", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_jid", "displayName": "group_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "group_name", "displayName": "group_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_jid", "displayName": "sender_jid", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_phone", "displayName": "sender_phone", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "sender_name", "displayName": "sender_name", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "country_code", "displayName": "country_code", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "pais_detectado", "displayName": "pais_detectado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "idioma_flujo", "displayName": "idioma_flujo", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "tipo_reporte", "displayName": "tipo_reporte", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ 8800, 1440 ], "id": "49036fad-dbd7-44d8-8597-b098eadf0ed3", "name": "Sheets - Actualizar sesión análisis completado WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst normalizado = getNodeJson('Code - Normalizar JSON final propuesta ejecutada TEST');\nconst presentacion = getNodeJson('Code - Normalizar link presentación ejecución TEST');\nconst basePaso = getNodeJson('Code - Resolver paso activo WhatsApp TEST');\nconst actual = $json || {};\n\nlet data = {\n ...basePaso,\n ...normalizado,\n ...actual,\n ...presentacion,\n};\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst destinoBase = limpiar(\n data.whatsapp_to ||\n data.group_jid ||\n data.whatsapp_remote_jid ||\n data.sesion_activa?.group_jid ||\n data.sesion_activa?.whatsapp_to ||\n data.sesion_activa?.whatsapp_remote_jid ||\n data.manager_telefono ||\n data.sender_phone ||\n ''\n);\n\nif (!destinoBase) {\n throw new Error('No se encontró destino WhatsApp para enviar mensaje final.');\n}\n\nlet whatsappTo = destinoBase;\n\n// IMPORTANTE:\n// Si es grupo, conservar @g.us.\n// No convertirlo a @s.whatsapp.net.\nif (whatsappTo.includes('@g.us')) {\n whatsappTo = whatsappTo;\n} else if (whatsappTo.includes('@s.whatsapp.net')) {\n whatsappTo = whatsappTo;\n} else {\n whatsappTo = `${whatsappTo.replace(/\\D/g, '')}@s.whatsapp.net`;\n}\n\nconst presentacionUrl = limpiar(\n data.presentacion_ejecucion_url ||\n data.presentation_url ||\n ''\n);\n\nconst bloquePresentacion = presentacionUrl\n ? `\\n\\nPresentación:\\n${presentacionUrl}`\n : '';\n\nconst idioma = limpiar(\n data.idioma_flujo ||\n data.sesion_activa?.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nlet whatsappText = '';\n\nif (idioma === 'EN') {\n whatsappText = `✅ Report received and processed successfully.\n\nYour evidence was registered in the proposal evidence bank.${bloquePresentacion}\n\nThank you for completing the report.`;\n} else {\n whatsappText = `✅ Reporte recibido y procesado correctamente.\n\nTu evidencia fue registrada en el banco de propuestas.${bloquePresentacion}\n\nGracias por completar el reporte.`;\n}\n\nreturn [\n {\n json: {\n ...data,\n\n whatsapp_to: whatsappTo,\n whatsapp_text: whatsappText,\n },\n },\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 9088, 1408 ], "id": "914441cf-483f-4aab-a30e-7c619a34e49e", "name": "Code - Preparar mensaje final WhatsApp TEST" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "7591a797-d76a-48e7-8e75-9a41cff6e974", "leftValue": "={{ Number($json.media_index || 0) === Number($json.media_total || 0) }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ -1600, 1360 ], "id": "9f291944-bbf8-4584-b997-9e842caca9cb", "name": "IF - Es último análisis media WhatsApp TEST" }, { "parameters": { "amount": 15 }, "type": "n8n-nodes-base.wait", "typeVersion": 1.1, "position": [ -1376, 1360 ], "id": "fef0aeef-418b-419d-a6ca-6a9440494139", "name": "Wait", "webhookId": "c6d9670c-1a1f-43c9-89b3-9952b1501850" }, { "parameters": { "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 737153956, "mode": "list", "cachedResultName": "propuestas_ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=737153956" }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ 384, 1376 ], "id": "7c547eeb-0528-460f-a9a2-d3533e43bb55", "name": "Sheets - Leer propuestas ejecutadas final TEST", "alwaysOutputData": true, "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const base = $('Code - Normalizar JSON final propuesta ejecutada TEST').first().json || {};\nconst rows = $input.all().map(item => item.json || {});\n\nconst sessionId = String(base.session_id || '').trim();\nconst ejecucionId = String(base.ejecucion_id || '').trim();\n\nconst existente = rows.find(row => {\n const rowSessionId = String(row.session_id || '').trim();\n const rowEjecucionId = String(row.ejecucion_id || '').trim();\n\n return (\n (sessionId && rowSessionId === sessionId) ||\n (ejecucionId && rowEjecucionId === ejecucionId)\n );\n});\n\nreturn [\n {\n json: {\n ...base,\n\n propuesta_final_ya_existe: Boolean(existente),\n propuesta_final_row_number: existente?.row_number || '',\n propuesta_final_existente: existente || null,\n\n estado_anti_duplicado: existente\n ? 'PROPUESTA_FINAL_YA_EXISTE'\n : 'PROPUESTA_FINAL_NUEVA'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 592, 1376 ], "id": "dc39153f-ddcc-4f21-abc8-7a4b42074807", "name": "Code - Verificar duplicado propuesta ejecutada TEST" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "80785332-9e50-44d5-9fd5-ecf9610876fd", "leftValue": "={{ $json.propuesta_final_ya_existe === true }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ 800, 1376 ], "id": "210119ea-e335-4d9d-abd2-b2aa5b0af24a", "name": "IF - Propuesta final ya existe TEST" }, { "parameters": { "jsCode": "const data = $('Code - Normalizar JSON final propuesta ejecutada TEST').first().json || {};\n\nconst templateId = '10PZtoMFuiXczGM9Itbcjce4EKBUhiztFbXZiTWX2DKc';\nconst folderId = '1q92b3lMw_fcjD1YUtp5YcJKne49zOfpc';\n\nconst limpiar = (valor) =>\n String(valor || '')\n .replace(/[\\\\/:*?\"<>|]/g, '-')\n .replace(/\\s+/g, ' ')\n .trim();\n\nconst marca = limpiar(data.marca || 'Marca no identificada');\nconst referencia = limpiar(data.propuesta_referencia || 'Propuesta ejecutada');\nconst fecha = limpiar(data.fecha_ejecucion || new Date().toISOString().slice(0, 10));\nconst sessionId = limpiar(data.session_id || data.ejecucion_id || Date.now());\n\nconst presentationName = `${fecha} - ${marca} - ${referencia} - ${sessionId}`;\n\nreturn [\n {\n json: {\n ...data,\n\n slides_template_id: templateId,\n slides_folder_id: folderId,\n presentation_name: presentationName,\n\n estado_presentacion: 'PRESENTACION_PREPARADA'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 2528, 1504 ], "id": "d7e6d879-f330-4944-a006-aa5735fe0177", "name": "Code - Preparar copia presentación ejecución TEST" }, { "parameters": { "operation": "copy", "fileId": { "__rl": true, "value": "={{ $json.slides_template_id }}", "mode": "id" }, "name": "={{ $json.presentation_name }}", "sameFolder": false, "driveId": { "__rl": true, "mode": "list", "value": "My Drive" }, "folderId": { "__rl": true, "value": "={{ $json.execution_folder_id }}", "mode": "id" }, "options": {} }, "type": "n8n-nodes-base.googleDrive", "typeVersion": 3, "position": [ 4448, 1552 ], "id": "f3543da5-0b8b-4de9-a934-8ed6daaa849c", "name": "Drive - Copiar plantilla presentación ejecución TEST", "credentials": { "googleDriveOAuth2Api": { "id": "g23xdGLZRzBGqKgH", "name": "Isaac - Google Drive" } } }, { "parameters": { "jsCode": "const base = $('Code - Preparar copia presentación ejecución TEST').first().json || {};\nconst drive = $json || {};\n\nconst presentationId =\n drive.id ||\n drive.fileId ||\n drive.presentationId ||\n drive.data?.id ||\n '';\n\nif (!presentationId) {\n throw new Error('No se encontró el ID de la presentación copiada.');\n}\n\nconst presentationUrl =\n drive.webViewLink ||\n drive.webUrl ||\n `https://docs.google.com/presentation/d/${presentationId}/edit`;\n\nreturn [\n {\n json: {\n ...base,\n\n presentation_id: presentationId,\n presentacion_ejecucion_url: presentationUrl,\n\n estado_presentacion: 'PRESENTACION_CREADA'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 4720, 1552 ], "id": "1b0f80c2-16cd-499a-aadf-3f44c3753ce4", "name": "Code - Normalizar link presentación ejecución TEST" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 737153956, "mode": "list", "cachedResultName": "propuestas_ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=737153956" }, "columns": { "mappingMode": "defineBelow", "value": { "presentacion_ejecucion_url": "={{ $json.presentacion_ejecucion_url }}", "ultima_actualizacion": "={{ new Date().toISOString() }}", "ejecucion_id": "={{ $json.ejecucion_id }}", "session_id": "={{ $json.session_id }}", "media_folder_url": "={{ $json.media_folder_url }}" }, "matchingColumns": [ "session_id" ], "schema": [ { "id": "ejecucion_id", "displayName": "ejecucion_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_recepcion", "displayName": "fecha_recepcion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fecha_ejecucion", "displayName": "fecha_ejecucion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_nombre", "displayName": "manager_nombre", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "manager_telefono", "displayName": "manager_telefono", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_referencia", "displayName": "propuesta_referencia", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_match_estado", "displayName": "propuesta_match_estado", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_match_confianza", "displayName": "propuesta_match_confianza", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_nombre_banco", "displayName": "propuesta_nombre_banco", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_link_banco", "displayName": "propuesta_link_banco", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "marca", "displayName": "marca", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "cliente", "displayName": "cliente", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "pais", "displayName": "pais", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ubicacion", "displayName": "ubicacion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "comentario_original", "displayName": "comentario_original", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "resumen_ia", "displayName": "resumen_ia", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "descripcion_ejecucion", "displayName": "descripcion_ejecucion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "elementos_detectados", "displayName": "elementos_detectados", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "tags", "displayName": "tags", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "media_folder_url", "displayName": "media_folder_url", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "presentacion_ejecucion_url", "displayName": "presentacion_ejecucion_url", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "fotos_count", "displayName": "fotos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "videos_count", "displayName": "videos_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "audios_count", "displayName": "audios_count", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "estado_revision", "displayName": "estado_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "motivo_revision", "displayName": "motivo_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "ultima_actualizacion", "displayName": "ultima_actualizacion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "session_id", "displayName": "session_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": false }, { "id": "canal_origen", "displayName": "canal_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "transcripcion_audio", "displayName": "transcripcion_audio", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "propuesta_match_revision", "displayName": "propuesta_match_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": true } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ 6480, 1424 ], "id": "cb35219a-46a0-49c2-a404-91c5c91a30d2", "name": "Sheets - Actualizar link presentación ejecución TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const data = $json || {};\n\nconst limpiarTexto = (valor, fallback = 'No identificado') => {\n const texto = String(valor ?? '').trim();\n\n if (!texto) return fallback;\n if (texto.toLowerCase() === 'undefined') return fallback;\n if (texto.toLowerCase() === 'null') return fallback;\n\n return texto;\n};\n\nconst cortar = (valor, max = 900) => {\n const texto = limpiarTexto(valor, 'No disponible');\n return texto.length > max ? texto.slice(0, max - 3) + '...' : texto;\n};\n\nconst normalizarTipoReporte = (valor) => {\n const texto = limpiarTexto(valor, '')\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toUpperCase()\n .replace(/\\s+/g, '_');\n\n if (\n texto === 'PROPUESTA_EXTERNA' ||\n texto === 'EXTERNA' ||\n texto === 'REPORTE_EXTERNO'\n ) {\n return 'PROPUESTA_EXTERNA';\n }\n\n if (\n texto === 'PROPUESTA_EJECUTADA' ||\n texto === 'EJECUTADA' ||\n texto === 'REPORTE_EJECUTADO'\n ) {\n return 'PROPUESTA_EJECUTADA';\n }\n\n return 'PROPUESTA_EJECUTADA';\n};\n\nconst tipoReporte = normalizarTipoReporte(\n data.tipo_reporte ||\n data.tipo_reporte_detectado ||\n data.reporte_tipo ||\n data.clasificacion_reporte ||\n ''\n);\n\nconst textoTipoReporte = tipoReporte === 'PROPUESTA_EXTERNA'\n ? 'Propuesta Externa'\n : 'Propuesta Ejecutada';\n\nconst tituloReporteTipo = tipoReporte === 'PROPUESTA_EXTERNA'\n ? 'REPORTE DE PROPUESTA EXTERNA'\n : 'REPORTE DE PROPUESTA EJECUTADA';\n\nconst encabezadoTipoReporte = `Fulgencio Fumado · ${textoTipoReporte}`;\n\nconst replacements = {\n '{{TITULO_REPORTE_TIPO}}': tituloReporteTipo,\n '{{ENCABEZADO_TIPO_REPORTE}}': encabezadoTipoReporte,\n '{{TIPO_REPORTE}}': textoTipoReporte,\n\n '{{MARCA}}': limpiarTexto(data.marca),\n '{{CLIENTE}}': limpiarTexto(data.cliente),\n '{{PAIS}}': limpiarTexto(data.pais),\n '{{UBICACION}}': limpiarTexto(data.ubicacion),\n\n '{{FECHA_EJECUCION}}': limpiarTexto(data.fecha_ejecucion),\n '{{PROPUESTA_REFERENCIA}}': limpiarTexto(data.propuesta_referencia),\n\n // Se dejan en blanco para no mostrar IDs técnicos en la presentación.\n // Los labels fijos \"ID de Ejecución\" / \"Ejecución ID\" deben eliminarse en la plantilla.\n '{{EJECUCION_ID}}': '',\n '{{SESSION_ID}}': '',\n\n '{{MANAGER_NOMBRE}}': limpiarTexto(data.manager_nombre),\n '{{MANAGER_TELEFONO}}': limpiarTexto(data.manager_telefono),\n\n '{{RESUMEN_IA}}': cortar(data.resumen_ia, 1000),\n '{{DESCRIPCION_EJECUCION}}': cortar(data.descripcion_ejecucion, 1000),\n '{{ELEMENTOS_DETECTADOS}}': cortar(data.elementos_detectados, 1000),\n '{{TAGS}}': cortar(data.tags, 500),\n\n '{{FOTOS_COUNT}}': limpiarTexto(data.fotos_count, '0'),\n '{{VIDEOS_COUNT}}': limpiarTexto(data.videos_count, '0'),\n '{{AUDIOS_COUNT}}': limpiarTexto(data.audios_count, '0'),\n\n '{{MEDIA_FOLDER_URL}}': limpiarTexto(data.media_folder_url, 'No disponible'),\n '{{LINK_PRESENTACION}}': limpiarTexto(data.presentacion_ejecucion_url, 'No disponible')\n};\n\nconst slidesReplaceRequests = Object.entries(replacements).map(([placeholder, value]) => {\n return {\n replaceAllText: {\n containsText: {\n text: placeholder,\n matchCase: true\n },\n replaceText: value\n }\n };\n});\n\nif (!data.presentation_id) {\n throw new Error('No llegó presentation_id para reemplazar textos en Slides.');\n}\n\nreturn [\n {\n json: {\n ...data,\n\n tipo_reporte_normalizado: tipoReporte,\n tipo_reporte_texto: textoTipoReporte,\n titulo_reporte_tipo: tituloReporteTipo,\n encabezado_tipo_reporte: encabezadoTipoReporte,\n\n slides_replacements: replacements,\n slides_replace_requests: slidesReplaceRequests,\n\n estado_presentacion: 'REEMPLAZOS_SLIDES_PREPARADOS'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 4928, 1552 ], "id": "72e00ab4-ed9e-4852-a89f-fdef9ba6551d", "name": "Code - Preparar reemplazos Slides ejecución TEST" }, { "parameters": { "method": "POST", "url": "={{ 'https://slides.googleapis.com/v1/presentations/' + $json.presentation_id + ':batchUpdate' }}", "authentication": "predefinedCredentialType", "nodeCredentialType": "googleOAuth2Api", "sendBody": true, "specifyBody": "json", "jsonBody": "={{ { requests: $json.slides_replace_requests } }}", "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ 5616, 1536 ], "id": "447d49f8-bac0-4c12-bbd0-89dbd2a43b2c", "name": "HTTP Request - Reemplazar textos Slides ejecución TEST", "credentials": { "googleOAuth2Api": { "id": "eHseMeH39kRcXgOF", "name": "Google account 2" } } }, { "parameters": { "jsCode": "const base = $('Code - Preparar reemplazos Slides ejecución TEST').first().json || {};\nconst respuestaSlides = $json || {};\n\nreturn [\n {\n json: {\n ...base,\n\n slides_batch_update_response: respuestaSlides,\n estado_presentacion: 'TEXTOS_SLIDES_REEMPLAZADOS'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 6224, 1536 ], "id": "9c252b51-c657-4020-bae8-fd79824feb32", "name": "Code - Normalizar respuesta Slides ejecución TEST" }, { "parameters": { "jsCode": "const actual = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst base = getNodeJson('Code - Resolver sesión WhatsApp TEST');\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst idioma = limpiar(\n actual.idioma_flujo ||\n base.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst destinoBase = limpiar(\n base.whatsapp_to ||\n base.group_jid ||\n actual.whatsapp_to ||\n actual.group_jid ||\n base.whatsapp_remote_jid ||\n actual.whatsapp_remote_jid ||\n base.manager_telefono ||\n actual.manager_telefono ||\n base.sender_phone ||\n actual.sender_phone ||\n ''\n);\n\nif (!destinoBase) {\n throw new Error('No se encontró destino WhatsApp para enviar mensaje de cancelación sin sesión.');\n}\n\nlet whatsappTo = destinoBase;\n\nif (whatsappTo.includes('@g.us')) {\n whatsappTo = whatsappTo;\n} else if (whatsappTo.includes('@s.whatsapp.net')) {\n whatsappTo = whatsappTo;\n} else {\n whatsappTo = `${whatsappTo.replace(/\\D/g, '')}@s.whatsapp.net`;\n}\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n 'ℹ️ *There is no active report to cancel.*',\n '',\n 'You can start a new evidence registration whenever you are ready.',\n '',\n 'Write:',\n '',\n '*Hey*'\n ].join('\\n');\n} else {\n mensaje = [\n 'ℹ️ *No hay un reporte activo para cancelar.*',\n '',\n 'Puedes iniciar un nuevo registro de evidencias cuando estés listo.',\n '',\n 'Escribe:',\n '',\n '*Hey*'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...base,\n ...actual,\n\n whatsapp_to: whatsappTo,\n whatsapp_text: mensaje\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5440, -1792 ], "id": "be56aeb8-916d-46b2-b2a2-b544a4bdbd01", "name": "Code - Preparar sin sesión para cancelar WhatsApp TEST" }, { "parameters": { "documentId": { "__rl": true, "value": "1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk", "mode": "list", "cachedResultName": "Fulgencio - Propuestas Ejecutadas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": 306129743, "mode": "list", "cachedResultName": "wa_ejecuciones_eventos", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1nbeuaW2lxvWJYVFD-E0uJRhTGzUa-nuH2WYItHdTsKk/edit#gid=306129743" }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -5392, -16 ], "id": "cc41848d-df63-499a-ba01-16155e30fffe", "name": "Sheets - Leer eventos sesión fotos WhatsApp TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const data = $json || {};\n\nconst limpiarTexto = (valor, fallback = 'No identificado') => {\n const texto = String(valor ?? '').trim();\n\n if (!texto) return fallback;\n if (texto.toLowerCase() === 'undefined') return fallback;\n if (texto.toLowerCase() === 'null') return fallback;\n\n return texto;\n};\n\nconst limpiarNombreArchivo = (valor) => {\n return limpiarTexto(valor)\n .replace(/[\\\\/:*?\"<>|#%{}~&]/g, '-')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, 120);\n};\n\nconst fechaBase = limpiarTexto(\n data.fecha_ejecucion ||\n data.fecha_recepcion ||\n data.ultima_actualizacion ||\n new Date().toISOString()\n);\n\nconst fecha = fechaBase.slice(0, 10);\n\nconst marca = limpiarNombreArchivo(data.marca);\nconst cliente = limpiarNombreArchivo(data.cliente);\nconst sessionId = limpiarNombreArchivo(data.session_id || data.ejecucion_id);\n\nconst folderName = `${fecha} - ${marca} - ${cliente} - ${sessionId}`;\n\n// Carpeta base PROPUESTAS EJECUTADAS\nconst parentFolderId = '1q92b3lMw_fcjD1YUtp5YcJKne49zOfpc';\n\nreturn [\n {\n json: {\n ...data,\n\n execution_folder_name: folderName,\n execution_parent_folder_id: parentFolderId\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 2736, 1504 ], "id": "3a7cf341-e7e6-4d90-bdd0-9e1abde12b10", "name": "Code - Preparar carpeta ejecución TEST" }, { "parameters": { "resource": "folder", "name": "={{ $json.execution_folder_name }}", "driveId": { "__rl": true, "mode": "list", "value": "My Drive" }, "folderId": { "__rl": true, "value": "={{ $json.execution_parent_folder_id }}", "mode": "id" }, "options": {} }, "type": "n8n-nodes-base.googleDrive", "typeVersion": 3, "position": [ 2944, 1504 ], "id": "ba2e9788-c593-4097-96be-e7f6d447be69", "name": "Drive - Crear carpeta ejecución TEST", "credentials": { "googleDriveOAuth2Api": { "id": "g23xdGLZRzBGqKgH", "name": "Isaac - Google Drive" } } }, { "parameters": { "jsCode": "const carpeta = $json || {};\nconst base = $('Code - Preparar carpeta ejecución TEST').first().json || {};\n\nconst folderId =\n carpeta.id ||\n carpeta.fileId ||\n carpeta.folderId ||\n '';\n\nconst folderUrl = folderId\n ? `https://drive.google.com/drive/folders/${folderId}`\n : '';\n\nif (!folderId) {\n throw new Error('No se pudo obtener el ID de la carpeta de ejecución.');\n}\n\nreturn [\n {\n json: {\n ...base,\n\n execution_folder_id: folderId,\n execution_folder_url: folderUrl,\n\n media_folder_id: folderId,\n media_folder_url: folderUrl\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 3152, 1504 ], "id": "3861110f-3334-4e89-954f-b8d86f9b4708", "name": "Code - Normalizar carpeta ejecución TEST" }, { "parameters": { "jsCode": "const data = $json || {};\n\nconst texto = (valor) => String(valor ?? '').trim();\n\nconst folderId = texto(data.execution_folder_id || data.media_folder_id);\n\nif (!folderId) {\n throw new Error('No existe execution_folder_id/media_folder_id para mover la multimedia.');\n}\n\nconst archivos = [];\nconst vistos = new Set();\n\nconst extraerFileIdDesdeUrl = (valor) => {\n const url = texto(valor);\n if (!url) return '';\n\n // Formato: https://drive.google.com/file/d/FILE_ID/view\n const matchFile = url.match(/\\/file\\/d\\/([^/]+)/);\n if (matchFile?.[1]) return matchFile[1];\n\n // Formato: https://drive.google.com/open?id=FILE_ID\n const matchOpen = url.match(/[?&]id=([^&]+)/);\n if (matchOpen?.[1]) return matchOpen[1];\n\n // Si ya viene como ID limpio\n if (!url.includes('http') && url.length > 15) return url;\n\n return '';\n};\n\nconst separarLista = (valor) => {\n return texto(valor)\n .split(/[\\n,]+/)\n .map(v => texto(v))\n .filter(Boolean);\n};\n\nconst agregarArchivo = (fileIdOrUrl, tipo) => {\n const fileId = extraerFileIdDesdeUrl(fileIdOrUrl);\n\n if (!fileId) return;\n if (vistos.has(fileId)) return;\n\n vistos.add(fileId);\n\n archivos.push({\n json: {\n ...data,\n\n move_file_id: fileId,\n move_file_type: tipo,\n\n execution_folder_id: folderId,\n media_folder_id: folderId,\n media_folder_url: data.media_folder_url,\n execution_folder_url: data.execution_folder_url\n }\n });\n};\n\n// 1. Archivo actual del item, si existe\nagregarArchivo(data.drive_file_id, data.media_type || 'media');\n\n// 2. URLs acumuladas por tipo\nfor (const url of separarLista(data.audio_drive_urls)) {\n agregarArchivo(url, 'audio');\n}\n\nfor (const url of separarLista(data.imagenes_drive_urls)) {\n agregarArchivo(url, 'image');\n}\n\nfor (const url of separarLista(data.videos_drive_urls)) {\n agregarArchivo(url, 'video');\n}\n\n// 3. Si tienes otros campos futuros\nfor (const url of separarLista(data.media_drive_urls)) {\n agregarArchivo(url, 'media');\n}\n\nif (archivos.length === 0) {\n return [\n {\n json: {\n ...data,\n media_move_status: 'SIN_ARCHIVOS_PARA_MOVER',\n media_move_debug: {\n drive_file_id: data.drive_file_id || '',\n audio_drive_urls: data.audio_drive_urls || '',\n imagenes_drive_urls: data.imagenes_drive_urls || '',\n videos_drive_urls: data.videos_drive_urls || ''\n }\n }\n }\n ];\n}\n\nreturn archivos;" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 3360, 1504 ], "id": "1ccc2d93-f72d-453a-a1b8-7a204d5a5108", "name": "Code - Preparar media para mover carpeta TEST" }, { "parameters": { "operation": "move", "fileId": { "__rl": true, "value": "={{ $json.move_file_id }}", "mode": "id" }, "driveId": { "__rl": true, "mode": "list", "value": "My Drive" }, "folderId": { "__rl": true, "value": "={{ $json.execution_folder_id }}", "mode": "id" } }, "type": "n8n-nodes-base.googleDrive", "typeVersion": 3, "position": [ 3568, 1504 ], "id": "fa4bc0cf-498d-4332-8d12-72f4eb7e5ce4", "name": "Drive - Mover media a carpeta ejecución TEST", "credentials": { "googleDriveOAuth2Api": { "id": "g23xdGLZRzBGqKgH", "name": "Isaac - Google Drive" } } }, { "parameters": { "jsCode": "const movido = $json || {};\nconst base = $('Code - Preparar media para mover carpeta TEST').first().json || {};\n\nreturn [\n {\n json: {\n ...base,\n\n media_move_status: 'MEDIA_MOVIDA_A_CARPETA',\n moved_file_id: base.move_file_id,\n moved_file_type: base.move_file_type,\n moved_file_url: base.move_file_url,\n\n drive_move_response_id: movido.id || movido.fileId || base.move_file_id\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 3776, 1504 ], "id": "2b0b6839-7d9b-4dc1-9969-a5b3b015653e", "name": "Code - Confirmar media movida carpeta TEST" }, { "parameters": { "jsCode": "const actual = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst base = getNodeJson('Code - Preparar reemplazos Slides ejecución TEST');\n\nconst data = {\n ...base,\n ...actual,\n\n // Aseguramos estos campos desde el nodo base si el nodo Slides no los devuelve\n presentation_id: actual.presentation_id || base.presentation_id,\n imagenes_drive_urls: actual.imagenes_drive_urls || base.imagenes_drive_urls,\n imagen_drive_urls: actual.imagen_drive_urls || base.imagen_drive_urls,\n media_drive_urls: actual.media_drive_urls || base.media_drive_urls,\n};\n\nconst texto = (valor) => String(valor ?? '').trim();\n\nconst extraerFileIdDesdeUrl = (valor) => {\n const url = texto(valor);\n if (!url) return '';\n\n const matchFile = url.match(/\\/file\\/d\\/([^/]+)/);\n if (matchFile?.[1]) return matchFile[1];\n\n const matchOpen = url.match(/[?&]id=([^&]+)/);\n if (matchOpen?.[1]) return matchOpen[1];\n\n if (!url.includes('http') && url.length > 15) return url;\n\n return '';\n};\n\nconst separarLista = (valor) => {\n return texto(valor)\n .split(/[\\n,]+/)\n .map(v => texto(v))\n .filter(Boolean);\n};\n\nconst imagenesUrls = separarLista(\n data.imagenes_drive_urls ||\n data.imagen_drive_urls ||\n data.media_drive_urls ||\n ''\n);\n\nconst imageFileIds = imagenesUrls\n .map(extraerFileIdDesdeUrl)\n .filter(Boolean)\n .slice(0, 6);\n\nif (!data.presentation_id) {\n throw new Error('No llegó presentation_id para insertar imágenes en Slides.');\n}\n\nreturn [\n {\n json: {\n ...data,\n\n apps_script_insert_images_payload: {\n secret: 'glm_fulgencio_slides_2026_seguro',\n presentation_id: data.presentation_id,\n image_file_ids: imageFileIds\n },\n\n imagenes_para_slides_count: imageFileIds.length,\n imagenes_para_slides_ids: imageFileIds,\n\n estado_insertar_imagenes: 'PAYLOAD_INSERTAR_IMAGENES_PREPARADO'\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 5824, 1536 ], "id": "2606aa33-73fd-4af3-9497-cea6932760c9", "name": "Code - Preparar payload insertar imágenes Slides TEST" }, { "parameters": { "method": "POST", "url": "https://script.google.com/macros/s/AKfycbyw76HYjYMvq0KXA9IN5S8nRWk0drPkjKSK2SAbCL9Ha8is3vFZMyR6Ldby4c3YVs7J/exec", "sendBody": true, "specifyBody": "json", "jsonBody": "={{ $json.apps_script_insert_images_payload }}", "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ 6032, 1536 ], "id": "4ed89dd2-2849-419e-95e4-e061939e0002", "name": "HTTP - Insertar imágenes en Slides TEST" }, { "parameters": { "jsCode": "const actual = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst base = getNodeJson('Code - Resolver paso activo WhatsApp TEST');\nconst sesion = base.sesion_activa || actual.sesion_activa || {};\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst normalizarDestino = (valor) => {\n let raw = limpiar(valor);\n\n if (!raw) return '';\n\n raw = raw.replace('@c.us', '@s.whatsapp.net');\n\n if (raw.includes('@g.us')) return raw;\n\n if (raw.includes('@s.whatsapp.net')) {\n const numero = raw.replace('@s.whatsapp.net', '').replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n }\n\n const numero = raw.replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n};\n\nconst idioma = limpiar(\n actual.idioma_flujo ||\n base.idioma_flujo ||\n sesion.idioma_flujo ||\n 'ES'\n).toUpperCase();\n\nconst destinoBase =\n actual.whatsapp_to ||\n actual.group_jid ||\n actual.whatsapp_remote_jid ||\n base.whatsapp_to ||\n base.group_jid ||\n base.whatsapp_remote_jid ||\n sesion.group_jid ||\n sesion.whatsapp_to ||\n sesion.whatsapp_remote_jid ||\n sesion.manager_telefono ||\n base.manager_telefono ||\n actual.manager_telefono ||\n base.sender_phone ||\n actual.sender_phone ||\n '';\n\nif (!destinoBase) {\n throw new Error('No se encontró destino WhatsApp para enviar aviso de procesamiento.');\n}\n\nlet mensaje = '';\n\nif (idioma === 'EN') {\n mensaje = [\n '⏳ *Report received.*',\n '',\n 'We are now processing your evidence.',\n '',\n 'This may take a moment while we analyze the media, organize the files and generate the presentation.',\n '',\n 'I will send you the final link when everything is ready.'\n ].join('\\n');\n} else {\n mensaje = [\n '⏳ *Reporte recibido.*',\n '',\n 'Estamos procesando tus evidencias.',\n '',\n 'Esto puede tomar un momento mientras analizamos los archivos, organizamos la carpeta y generamos la presentación.',\n '',\n 'Te enviaré el link final cuando todo esté listo.'\n ].join('\\n');\n}\n\nreturn [\n {\n json: {\n ...base,\n ...actual,\n\n whatsapp_to: normalizarDestino(destinoBase),\n whatsapp_text: mensaje,\n\n aviso_procesando_preparado: true\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5936, 688 ], "id": "fdbbc5d5-a162-4bfa-ab41-47f4087dba08", "name": "Code - Preparar aviso procesando WhatsApp TEST1" }, { "parameters": { "jsCode": "const respuesta = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nlet base =\n getNodeJson('Code - Preparar aviso procesando WhatsApp TEST') ||\n getNodeJson('Code - Preparar aviso procesando WhatsApp TEST1') ||\n {};\n\nif (!base || Object.keys(base).length === 0) {\n base = getNodeJson('Code - Resolver paso activo WhatsApp TEST') || {};\n}\n\nreturn [\n {\n json: {\n ...base,\n\n aviso_procesando_enviado: true,\n aviso_procesando_response: respuesta\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -5520, 688 ], "id": "78fdf65a-3ff9-4219-b22f-9088baa65aeb", "name": "Code - Restaurar contexto aviso procesando TEST" }, { "parameters": { "method": "POST", "url": "={{'https://wsp.gomezleemarketing.com'}}/message/sendText/{{'botsoporte'}}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "apikey", "value": "={{'C267126ABB45-4C12-B626-6BAB1833F5D7'}}" } ] }, "sendBody": true, "bodyParameters": { "parameters": [ { "name": "number", "value": "={{ (() => {\n const limpiar = (valor) => String(valor ?? '').trim();\n\n const candidatos = [\n $json.whatsapp_to,\n $json.group_jid,\n $json.whatsapp_remote_jid,\n $json.sesion_activa?.group_jid,\n $json.sesion_activa?.whatsapp_to,\n $json.sesion_activa?.whatsapp_remote_jid,\n $json.manager_telefono,\n $json.sender_phone\n ];\n\n let raw = limpiar(candidatos.find(v => limpiar(v)));\n\n if (!raw) return '';\n\n raw = raw.replace('@c.us', '@s.whatsapp.net');\n\n // Si ya viene como grupo, conservarlo.\n if (raw.includes('@g.us')) {\n return raw;\n }\n\n // Si viene como usuario WhatsApp, validar si realmente era un grupo.\n if (raw.includes('@s.whatsapp.net')) {\n const numero = raw.replace('@s.whatsapp.net', '').replace(/\\D/g, '');\n\n // Los grupos de WhatsApp suelen venir como 120363...\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n }\n\n const numero = raw.replace(/\\D/g, '');\n\n if (numero.startsWith('120363')) {\n return `${numero}@g.us`;\n }\n\n return `${numero}@s.whatsapp.net`;\n})() }}" }, { "name": "delay", "value": "={{ 1000 }}" }, { "name": "text", "value": "={{ $json.whatsapp_text }}" } ] }, "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ -5744, 688 ], "id": "eeb80d93-128f-488e-814e-86170b8049f7", "name": "HTTP - Enviar aviso procesando WhatsApp TEST" }, { "parameters": { "documentId": { "__rl": true, "value": "1iy3lC3DS3ZfShb7d26iQOrPjx9s_3B-PC07XfYew7ng", "mode": "list", "cachedResultName": "BANCO DE PROPUESTAS DE CDC PARA FULGENCIO FUMADO", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iy3lC3DS3ZfShb7d26iQOrPjx9s_3B-PC07XfYew7ng/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": "gid=0", "mode": "list", "cachedResultName": "propuestas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iy3lC3DS3ZfShb7d26iQOrPjx9s_3B-PC07XfYew7ng/edit#gid=0" }, "options": { "dataLocationOnSheet": { "values": { "rangeDefinition": "detectAutomatically" } }, "outputFormatting": { "values": { "general": "UNFORMATTED_VALUE", "date": "FORMATTED_STRING" } }, "returnFirstMatch": false } }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ -208, 1360 ], "id": "0eb5f707-d1a2-4147-a195-a88ed9b5903e", "name": "Sheets - Leer banco propuestas Fulgencio TEST", "alwaysOutputData": true, "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst propuesta =\n getNodeJson('Code - Normalizar JSON final propuesta ejecutada TEST') ||\n $json ||\n {};\n\nconst bancoRows = $input.all().map(item => item.json || {});\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst normalizar = (valor) => {\n return limpiar(valor)\n .replace(/\\+/g, ' mas ')\n .replace(/_/g, ' ')\n .replace(/-/g, ' ')\n .replace(/\\//g, ' ')\n .replace(/\\./g, ' ')\n .replace(/\\bGUATE\\b/gi, ' Guatemala ')\n .replace(/\\bGUA\\b/gi, ' Guatemala ')\n .replace(/\\bGT\\b/gi, ' Guatemala ')\n .replace(/\\bRD\\b/gi, ' Republica Dominicana ')\n .replace(/\\bREP DOM\\b/gi, ' Republica Dominicana ')\n .replace(/\\bREP DOMINICANA\\b/gi, ' Republica Dominicana ')\n .replace(/\\bDOMINICANA\\b/gi, ' Republica Dominicana ')\n .replace(/\\bCR\\b/gi, ' Costa Rica ')\n .replace(/\\bPAN\\b/gi, ' Panama ')\n .replace(/\\bPTY\\b/gi, ' Panama ')\n .replace(/\\bSV\\b/gi, ' El Salvador ')\n .replace(/\\bSALVADOR\\b/gi, ' El Salvador ')\n .replace(/\\bHND\\b/gi, ' Honduras ')\n .replace(/\\bNIC\\b/gi, ' Nicaragua ')\n .replace(/\\bWMC\\b/gi, ' WMC ')\n .replace(/\\bRTM\\b/gi, ' RTM ')\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9\\s]/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n};\n\nconst tokens = (valor) => {\n const stopwords = new Set([\n 'de', 'del', 'la', 'el', 'los', 'las', 'y', 'en', 'para', 'por', 'con',\n 'una', 'un', 'uno', 'dos', 'tres', 'the', 'of', 'and', 'for', 'to', 'a',\n 'propuesta', 'presentacion', 'presentación', 'ejecucion', 'ejecución',\n 'ejecutada', 'ejecutado', 'reporte', 'evidencia', 'version', 'versión',\n 'archivo', 'link', 'slide', 'slides', 'prueba'\n ]);\n\n return normalizar(valor)\n .split(' ')\n .map(t => t.trim())\n .filter(Boolean)\n .filter(t => t.length >= 3 || /^v\\d+$/i.test(t))\n .filter(t => !stopwords.has(t));\n};\n\nconst tokenSet = (valor) => new Set(tokens(valor));\n\nconst similitudTokens = (a, b) => {\n const ta = tokenSet(a);\n const tb = tokenSet(b);\n\n if (ta.size === 0 || tb.size === 0) return 0;\n\n let interseccion = 0;\n\n for (const token of ta) {\n if (tb.has(token)) interseccion++;\n }\n\n const union = new Set([...ta, ...tb]).size;\n\n return union === 0 ? 0 : interseccion / union;\n};\n\nconst coberturaTokens = (textoBase, textoContra) => {\n const base = [...tokenSet(textoBase)];\n const contra = tokenSet(textoContra);\n\n if (base.length === 0) return 0;\n\n const encontrados = base.filter(t => contra.has(t));\n\n return encontrados.length / base.length;\n};\n\nconst contarTokensComunes = (a, b) => {\n const ta = tokenSet(a);\n const tb = tokenSet(b);\n\n let comunes = 0;\n\n for (const token of ta) {\n if (tb.has(token)) comunes++;\n }\n\n return comunes;\n};\n\nconst obtenerCampo = (row, posiblesNombres) => {\n const keys = Object.keys(row || {});\n\n for (const nombre of posiblesNombres) {\n const exacto = keys.find(k => normalizar(k) === normalizar(nombre));\n if (exacto && limpiar(row[exacto])) return limpiar(row[exacto]);\n }\n\n for (const nombre of posiblesNombres) {\n const parcial = keys.find(k => normalizar(k).includes(normalizar(nombre)));\n if (parcial && limpiar(row[parcial])) return limpiar(row[parcial]);\n }\n\n return '';\n};\n\nconst extraerVersion = (valor) => {\n const texto = normalizar(valor);\n const match = texto.match(/\\bv\\d+\\b/);\n return match ? match[0].toUpperCase() : '';\n};\n\nconst extraerAnio = (valor) => {\n const texto = limpiar(valor);\n const match = texto.match(/\\b20\\d{2}\\b/);\n return match ? match[0] : '';\n};\n\nconst contienePais = (texto, pais) => {\n const t = normalizar(texto);\n const p = normalizar(pais);\n\n if (!p) return false;\n\n if (t.includes(p)) return true;\n\n if (p.includes('guatemala') && t.includes('guatemala')) return true;\n if (p.includes('republica dominicana') && t.includes('republica dominicana')) return true;\n if (p.includes('costa rica') && t.includes('costa rica')) return true;\n if (p.includes('panama') && t.includes('panama')) return true;\n if (p.includes('salvador') && t.includes('salvador')) return true;\n if (p.includes('honduras') && t.includes('honduras')) return true;\n if (p.includes('nicaragua') && t.includes('nicaragua')) return true;\n\n return false;\n};\n\nconst tipoReporte = limpiar(propuesta.tipo_reporte).toUpperCase();\n\nif (tipoReporte !== 'PROPUESTA_EJECUTADA') {\n return [\n {\n json: {\n ...propuesta,\n\n propuesta_match_estado:\n tipoReporte === 'PROPUESTA_EXTERNA'\n ? 'NO_APLICA_PROPUESTA_EXTERNA'\n : 'NO_APLICA_NO_DETERMINADO',\n\n propuesta_match_confianza: '0',\n propuesta_nombre_banco: '',\n propuesta_link_banco: '',\n propuesta_banco_row_number: '',\n propuesta_banco_enlaces_existentes: '',\n propuesta_banco_file_id: '',\n\n propuesta_match_revision:\n tipoReporte === 'PROPUESTA_EXTERNA'\n ? 'NO_REQUIERE_MATCH_BANCO'\n : 'REQUIERE_REVISION_TIPO_NO_DETERMINADO',\n\n match_banco_debug: {\n motivo: 'No se ejecutó match porque el tipo_reporte no es PROPUESTA_EJECUTADA.',\n tipo_reporte: tipoReporte || 'NO_DETERMINADO'\n }\n }\n }\n ];\n}\n\nconst referencia = limpiar(propuesta.propuesta_referencia);\nconst marca = limpiar(propuesta.marca);\nconst cliente = limpiar(propuesta.cliente);\nconst pais = limpiar(propuesta.pais);\nconst ubicacion = limpiar(propuesta.ubicacion);\nconst descripcion = limpiar(propuesta.descripcion_ejecucion || propuesta.resumen_ia);\nconst elementosDetectados = limpiar(propuesta.elementos_detectados);\nconst comentarioOriginal = limpiar(propuesta.comentario_original);\nconst transcripcionAudio = limpiar(propuesta.transcripcion_audio);\nconst textoAudio = limpiar(propuesta.texto_audio);\nconst textoImagenes = limpiar(propuesta.texto_imagenes);\nconst tags = limpiar(propuesta.tags);\n\nconst textoPropuestaCompleto = [\n referencia,\n marca,\n cliente,\n pais,\n ubicacion,\n descripcion,\n elementosDetectados,\n comentarioOriginal,\n transcripcionAudio,\n textoAudio,\n textoImagenes,\n tags\n].filter(Boolean).join(' ');\n\nconst textoClavePropuesta = [\n referencia,\n marca,\n cliente,\n pais,\n elementosDetectados,\n textoImagenes,\n tags\n].filter(Boolean).join(' ');\n\nconst versionPropuesta = extraerVersion(textoPropuestaCompleto);\nconst anioPropuesta = extraerAnio(textoPropuestaCompleto);\n\nconst candidatos = [];\n\nfor (const [index, row] of bancoRows.entries()) {\n const bancoRowNumber = String(index + 2);\n\n const bancoNombre = obtenerCampo(row, [\n 'NOMBRE',\n 'propuesta',\n 'nombre propuesta',\n 'nombre de propuesta',\n 'titulo',\n 'título',\n 'nombre',\n 'referencia',\n 'brief',\n 'idea'\n ]);\n\n const bancoTipoAccion = obtenerCampo(row, [\n 'TIPO DE ACCION',\n 'TIPO DE ACCIÓN',\n 'tipo accion',\n 'tipo acción'\n ]);\n\n const bancoCliente = obtenerCampo(row, [\n 'CLIENTE',\n 'cliente',\n 'client'\n ]);\n\n const bancoMarca = obtenerCampo(row, [\n 'MARCA',\n 'marca',\n 'brand'\n ]);\n\n const bancoPais = obtenerCampo(row, [\n 'PAIS',\n 'PAÍS',\n 'pais',\n 'país',\n 'country'\n ]);\n\n const bancoCanal = obtenerCampo(row, [\n 'CANAL',\n 'canal'\n ]);\n\n const bancoAmbiente = obtenerCampo(row, [\n 'AMBIENTE DE COMPRA',\n 'ambiente de compra',\n 'ambiente'\n ]);\n\n const bancoDescripcion = obtenerCampo(row, [\n 'Descripcion',\n 'Descripción',\n 'descripcion',\n 'descripción',\n 'detalle',\n 'mecanica',\n 'mecánica',\n 'comentario',\n 'observacion',\n 'observación'\n ]);\n\n const bancoLink = obtenerCampo(row, [\n 'Enlace a la propuesta',\n 'link',\n 'enlace',\n 'url',\n 'brief link',\n 'link brief',\n 'presentacion',\n 'presentación',\n 'drive'\n ]);\n\n const bancoFileId = obtenerCampo(row, [\n 'file_id',\n 'File ID',\n 'archivo_id'\n ]);\n\n const bancoEnlacesEjecutadas = obtenerCampo(row, [\n 'Enlaces a propuestas ejecutadas',\n 'Enlace a propuestas ejecutadas',\n 'Links propuestas ejecutadas',\n 'Link propuestas ejecutadas',\n 'Ejecuciones',\n 'Propuestas ejecutadas'\n ]);\n\n if (!bancoNombre && !bancoMarca && !bancoCliente && !bancoDescripcion) {\n continue;\n }\n\n const textoBancoCompleto = [\n bancoNombre,\n bancoTipoAccion,\n bancoCliente,\n bancoMarca,\n bancoPais,\n bancoCanal,\n bancoAmbiente,\n bancoDescripcion\n ].filter(Boolean).join(' ');\n\n const textoClaveBanco = [\n bancoNombre,\n bancoCliente,\n bancoMarca,\n bancoPais\n ].filter(Boolean).join(' ');\n\n const scoreReferenciaNombre = similitudTokens(referencia, bancoNombre);\n const scoreEvidenciaNombre = similitudTokens(textoClavePropuesta, bancoNombre);\n const coberturaNombreEnEvidencia = coberturaTokens(bancoNombre, textoPropuestaCompleto);\n const coberturaReferenciaEnBanco = coberturaTokens(referencia, textoBancoCompleto);\n\n const scoreNombre = Math.max(\n scoreReferenciaNombre,\n scoreEvidenciaNombre,\n coberturaNombreEnEvidencia,\n coberturaReferenciaEnBanco\n );\n\n const scoreMarcaContraBanco = Math.max(\n similitudTokens(marca, bancoMarca),\n similitudTokens(marca, bancoNombre),\n similitudTokens(marca, textoBancoCompleto)\n );\n\n const scoreClienteContraBanco = Math.max(\n similitudTokens(cliente, bancoCliente),\n similitudTokens(cliente, bancoNombre),\n similitudTokens(cliente, textoBancoCompleto)\n );\n\n const scorePais = Math.max(\n similitudTokens(pais, bancoPais),\n contienePais(bancoNombre, pais) ? 1 : 0,\n contienePais(textoBancoCompleto, pais) ? 1 : 0\n );\n\n const scoreGeneral = similitudTokens(textoPropuestaCompleto, textoBancoCompleto);\n const scoreClave = similitudTokens(textoClavePropuesta, textoClaveBanco);\n const coberturaBancoEnPropuesta = coberturaTokens(bancoNombre, textoPropuestaCompleto);\n const comunesNombreEvidencia = contarTokensComunes(bancoNombre, textoPropuestaCompleto);\n\n const versionBanco = extraerVersion(textoBancoCompleto);\n const anioBanco = extraerAnio(textoBancoCompleto);\n\n const versionCoincide =\n versionPropuesta &&\n versionBanco &&\n versionPropuesta === versionBanco;\n\n const versionContradice =\n versionPropuesta &&\n versionBanco &&\n versionPropuesta !== versionBanco;\n\n const anioCoincide =\n anioPropuesta &&\n anioBanco &&\n anioPropuesta === anioBanco;\n\n const anioContradice =\n anioPropuesta &&\n anioBanco &&\n anioPropuesta !== anioBanco;\n\n const paisCoincide =\n scorePais >= 0.80 ||\n contienePais(bancoNombre, pais) ||\n contienePais(textoBancoCompleto, pais);\n\n const marcaCoincide =\n scoreMarcaContraBanco >= 0.45 ||\n normalizar(textoBancoCompleto).includes(normalizar(marca)) ||\n normalizar(textoPropuestaCompleto).includes(normalizar(bancoMarca));\n\n const scorePonderado =\n scoreNombre * 0.46 +\n scoreClave * 0.22 +\n scoreMarcaContraBanco * 0.08 +\n scoreClienteContraBanco * 0.04 +\n scorePais * 0.10 +\n scoreGeneral * 0.10;\n\n let scoreFinal = scorePonderado;\n let reglaMatch = 'PONDERADO';\n\n // Regla 1: el nombre/referencia de la propuesta está claramente contenido en la evidencia.\n if (coberturaBancoEnPropuesta >= 0.85 && (paisCoincide || anioCoincide || versionCoincide)) {\n scoreFinal = Math.max(scoreFinal, versionCoincide ? 0.97 : 0.93);\n reglaMatch = versionCoincide\n ? 'NOMBRE_BANCO_EN_EVIDENCIA_CON_VERSION'\n : 'NOMBRE_BANCO_EN_EVIDENCIA';\n }\n\n // Regla 2: combinación fuerte de clave propuesta-banco.\n if (scoreClave >= 0.85 && (paisCoincide || anioCoincide || marcaCoincide)) {\n scoreFinal = Math.max(scoreFinal, 0.95);\n reglaMatch = 'CLAVE_COMPLETA_PROPUESTA_BANCO';\n } else if (scoreClave >= 0.75 && (paisCoincide || versionCoincide)) {\n scoreFinal = Math.max(scoreFinal, 0.90);\n reglaMatch = 'CLAVE_FUERTE_PROPUESTA_BANCO';\n }\n\n // Regla 3: referencia parcial + país + versión.\n if (scoreNombre >= 0.60 && paisCoincide && versionCoincide) {\n scoreFinal = Math.max(scoreFinal, 0.94);\n reglaMatch = 'REFERENCIA_PAIS_VERSION';\n }\n\n // Regla 4: muchos tokens comunes, aunque Gemini haya escrito el nombre distinto.\n if (comunesNombreEvidencia >= 5 && (paisCoincide || anioCoincide)) {\n scoreFinal = Math.max(scoreFinal, versionCoincide ? 0.96 : 0.88);\n reglaMatch = versionCoincide\n ? 'TOKENS_COMUNES_PAIS_VERSION'\n : 'TOKENS_COMUNES_PAIS';\n }\n\n // Regla 5: para casos como RTM+ vs RTM más, GUATE vs Guatemala.\n if (\n normalizar(textoPropuestaCompleto).includes('rtm') &&\n normalizar(textoBancoCompleto).includes('rtm') &&\n normalizar(textoPropuestaCompleto).includes('wmc') &&\n normalizar(textoBancoCompleto).includes('wmc') &&\n (paisCoincide || normalizar(textoBancoCompleto).includes('guatemala')) &&\n (marcaCoincide || normalizar(textoBancoCompleto).includes('campero'))\n ) {\n scoreFinal = Math.max(scoreFinal, versionCoincide ? 0.96 : 0.89);\n reglaMatch = versionCoincide\n ? 'RTM_WMC_MARCA_PAIS_VERSION'\n : 'RTM_WMC_MARCA_PAIS';\n }\n\n // Penalizaciones de seguridad.\n if (anioContradice) {\n scoreFinal = Math.min(scoreFinal, 0.59);\n reglaMatch = `${reglaMatch}_PENALIZADO_ANIO`;\n }\n\n if (versionContradice && scoreFinal >= 0.85) {\n scoreFinal = Math.min(scoreFinal, 0.79);\n reglaMatch = `${reglaMatch}_PENALIZADO_VERSION`;\n }\n\n // Si no coincide ni país, ni marca, ni año, no lo dejamos como alta confianza.\n if (\n scoreFinal >= 0.85 &&\n !paisCoincide &&\n !marcaCoincide &&\n !anioCoincide\n ) {\n scoreFinal = 0.74;\n reglaMatch = `${reglaMatch}_PENALIZADO_SIN_ANCLA`;\n }\n\n let bonusOrden = 0;\n\n if (versionCoincide) bonusOrden += 0.04;\n if (anioCoincide) bonusOrden += 0.02;\n if (paisCoincide) bonusOrden += 0.02;\n if (marcaCoincide) bonusOrden += 0.01;\n if (bancoFileId) bonusOrden += 0.005;\n\n candidatos.push({\n row,\n score: scoreFinal,\n score_orden: scoreFinal + bonusOrden,\n score_ponderado: scorePonderado,\n score_porcentaje: Math.round(scoreFinal * 100),\n\n score_nombre: Math.round(scoreNombre * 100),\n score_referencia_nombre: Math.round(scoreReferenciaNombre * 100),\n score_evidencia_nombre: Math.round(scoreEvidenciaNombre * 100),\n cobertura_nombre_en_evidencia: Math.round(coberturaNombreEnEvidencia * 100),\n cobertura_referencia_en_banco: Math.round(coberturaReferenciaEnBanco * 100),\n score_marca: Math.round(scoreMarcaContraBanco * 100),\n score_cliente: Math.round(scoreClienteContraBanco * 100),\n score_pais: Math.round(scorePais * 100),\n score_general: Math.round(scoreGeneral * 100),\n score_clave: Math.round(scoreClave * 100),\n cobertura_banco_en_propuesta: Math.round(coberturaBancoEnPropuesta * 100),\n comunes_nombre_evidencia: comunesNombreEvidencia,\n\n version_propuesta: versionPropuesta,\n version_banco: versionBanco,\n version_coincide: Boolean(versionCoincide),\n version_contradice: Boolean(versionContradice),\n\n anio_propuesta: anioPropuesta,\n anio_banco: anioBanco,\n anio_coincide: Boolean(anioCoincide),\n anio_contradice: Boolean(anioContradice),\n\n pais_coincide: Boolean(paisCoincide),\n marca_coincide: Boolean(marcaCoincide),\n\n regla_match: reglaMatch,\n\n banco_row_number: bancoRowNumber,\n banco_nombre: bancoNombre,\n banco_tipo_accion: bancoTipoAccion,\n banco_marca: bancoMarca,\n banco_cliente: bancoCliente,\n banco_pais: bancoPais,\n banco_canal: bancoCanal,\n banco_link: bancoLink,\n banco_file_id: bancoFileId,\n banco_enlaces_ejecutadas: bancoEnlacesEjecutadas\n });\n}\n\nconst candidatosValidos = candidatos\n .filter(c => c.score > 0)\n .sort((a, b) => {\n if (b.score_orden !== a.score_orden) return b.score_orden - a.score_orden;\n return Number(b.banco_row_number || 0) - Number(a.banco_row_number || 0);\n });\n\nconst mejor = candidatosValidos[0] || null;\nconst segundo = candidatosValidos[1] || null;\n\nlet propuestaMatchEstado = 'SIN_MATCH';\nlet propuestaMatchRevision = 'REQUIERE_REVISION_MANUAL';\nlet propuestaMatchConfianza = '0';\nlet propuestaNombreBanco = '';\nlet propuestaLinkBanco = '';\nlet propuestaBancoRowNumber = '';\nlet propuestaBancoEnlacesExistentes = '';\nlet propuestaBancoFileId = '';\n\nif (mejor) {\n let confianzaFinal = mejor.score_porcentaje;\n\n // Si el segundo está demasiado cerca, mantenemos match pero pedimos revisión.\n const segundoMuyCerca =\n segundo &&\n mejor.score_porcentaje >= 85 &&\n segundo.score_porcentaje >= 85 &&\n Math.abs(mejor.score_porcentaje - segundo.score_porcentaje) <= 2 &&\n normalizar(mejor.banco_nombre) !== normalizar(segundo.banco_nombre);\n\n propuestaNombreBanco = mejor.banco_nombre || 'No identificado';\n propuestaLinkBanco = mejor.banco_link || '';\n propuestaBancoRowNumber = mejor.banco_row_number || '';\n propuestaBancoEnlacesExistentes = mejor.banco_enlaces_ejecutadas || '';\n propuestaBancoFileId = mejor.banco_file_id || '';\n\n if (confianzaFinal >= 85 && !segundoMuyCerca) {\n propuestaMatchEstado = 'MATCH_ALTA_CONFIANZA';\n propuestaMatchRevision = 'NO_REQUIERE_REVISION';\n } else if (confianzaFinal >= 85 && segundoMuyCerca) {\n propuestaMatchEstado = 'MATCH_MEDIA_CONFIANZA';\n propuestaMatchRevision = 'REQUIERE_VALIDACION_MANUAL';\n confianzaFinal = Math.min(confianzaFinal, 84);\n } else if (confianzaFinal >= 60) {\n propuestaMatchEstado = 'MATCH_MEDIA_CONFIANZA';\n propuestaMatchRevision = 'REQUIERE_VALIDACION_MANUAL';\n } else {\n propuestaMatchEstado = 'MATCH_BAJA_CONFIANZA';\n propuestaMatchRevision = 'REQUIERE_REVISION_MANUAL';\n }\n\n propuestaMatchConfianza = String(confianzaFinal);\n}\n\nreturn [\n {\n json: {\n ...propuesta,\n\n propuesta_match_estado: propuestaMatchEstado,\n propuesta_match_confianza: propuestaMatchConfianza,\n propuesta_nombre_banco: propuestaNombreBanco,\n propuesta_link_banco: propuestaLinkBanco,\n propuesta_match_revision: propuestaMatchRevision,\n\n propuesta_banco_row_number: propuestaBancoRowNumber,\n propuesta_banco_enlaces_existentes: propuestaBancoEnlacesExistentes,\n propuesta_banco_file_id: propuestaBancoFileId,\n\n match_banco_debug: {\n total_filas_banco: bancoRows.length,\n total_candidatos: candidatosValidos.length,\n propuesta_normalizada: {\n referencia,\n marca,\n cliente,\n pais,\n version_propuesta: versionPropuesta,\n anio_propuesta: anioPropuesta,\n texto_clave: textoClavePropuesta\n },\n mejor_match: mejor\n ? {\n score: mejor.score_porcentaje,\n score_orden: Math.round(mejor.score_orden * 100),\n regla_match: mejor.regla_match,\n row_number: mejor.banco_row_number,\n nombre: mejor.banco_nombre,\n tipo_accion: mejor.banco_tipo_accion,\n marca: mejor.banco_marca,\n cliente: mejor.banco_cliente,\n pais: mejor.banco_pais,\n canal: mejor.banco_canal,\n link: mejor.banco_link,\n file_id: mejor.banco_file_id,\n enlaces_ejecutadas_existentes: mejor.banco_enlaces_ejecutadas,\n score_nombre: mejor.score_nombre,\n score_referencia_nombre: mejor.score_referencia_nombre,\n score_evidencia_nombre: mejor.score_evidencia_nombre,\n cobertura_nombre_en_evidencia: mejor.cobertura_nombre_en_evidencia,\n cobertura_referencia_en_banco: mejor.cobertura_referencia_en_banco,\n score_marca: mejor.score_marca,\n score_cliente: mejor.score_cliente,\n score_pais: mejor.score_pais,\n score_general: mejor.score_general,\n score_clave: mejor.score_clave,\n cobertura_banco_en_propuesta: mejor.cobertura_banco_en_propuesta,\n comunes_nombre_evidencia: mejor.comunes_nombre_evidencia,\n version_banco: mejor.version_banco,\n version_coincide: mejor.version_coincide,\n anio_banco: mejor.anio_banco,\n anio_coincide: mejor.anio_coincide,\n pais_coincide: mejor.pais_coincide,\n marca_coincide: mejor.marca_coincide\n }\n : null,\n segundo_match: segundo\n ? {\n score: segundo.score_porcentaje,\n regla_match: segundo.regla_match,\n row_number: segundo.banco_row_number,\n nombre: segundo.banco_nombre,\n marca: segundo.banco_marca,\n cliente: segundo.banco_cliente,\n pais: segundo.banco_pais,\n file_id: segundo.banco_file_id\n }\n : null,\n top_5: candidatosValidos.slice(0, 5).map(c => ({\n score: c.score_porcentaje,\n score_orden: Math.round(c.score_orden * 100),\n regla_match: c.regla_match,\n row_number: c.banco_row_number,\n nombre: c.banco_nombre,\n marca: c.banco_marca,\n cliente: c.banco_cliente,\n pais: c.banco_pais,\n file_id: c.banco_file_id,\n score_nombre: c.score_nombre,\n score_clave: c.score_clave,\n cobertura_banco_en_propuesta: c.cobertura_banco_en_propuesta,\n comunes_nombre_evidencia: c.comunes_nombre_evidencia,\n version_banco: c.version_banco,\n version_coincide: c.version_coincide,\n anio_banco: c.anio_banco,\n anio_coincide: c.anio_coincide\n }))\n }\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 0, 1360 ], "id": "2043ba80-2789-4cc7-a261-943c464dd2d6", "name": "Code - Match propuesta banco Fulgencio TEST" }, { "parameters": { "jsCode": "const actual = $json || {};\n\nconst getNodeJson = (nodeName) => {\n try {\n return $(nodeName).first().json || {};\n } catch (error) {\n return {};\n }\n};\n\nconst limpiar = (valor) => String(valor ?? '').trim();\n\nconst jsonFinal = getNodeJson('Code - Normalizar JSON final propuesta ejecutada TEST');\nconst match = getNodeJson('Code - Match propuesta banco Fulgencio TEST');\n\nconst slidesNormalizado1 = getNodeJson('Code - Normalizar respuesta Slides TEST');\nconst slidesNormalizado2 = getNodeJson('Code - Normalizar respuesta Slides ejecución TEST');\nconst slidesNormalizado3 = getNodeJson('Code - Normalizar link presentación ejecución TEST');\n\nconst data = {\n ...jsonFinal,\n ...actual,\n ...slidesNormalizado1,\n ...slidesNormalizado2,\n ...slidesNormalizado3,\n ...match\n};\n\nconst matchDebug =\n match.match_banco_debug ||\n actual.match_banco_debug ||\n data.match_banco_debug ||\n {};\n\nconst mejorMatch = matchDebug.mejor_match || {};\n\nconst tipoReporte = limpiar(\n match.tipo_reporte ||\n data.tipo_reporte ||\n actual.tipo_reporte ||\n ''\n).toUpperCase();\n\nlet matchEstado = limpiar(\n match.propuesta_match_estado ||\n data.propuesta_match_estado ||\n actual.propuesta_match_estado ||\n ''\n).toUpperCase();\n\nlet matchConfianza = Number(\n match.propuesta_match_confianza ||\n data.propuesta_match_confianza ||\n actual.propuesta_match_confianza ||\n mejorMatch.score ||\n 0\n);\n\nlet nombreBanco = limpiar(\n match.propuesta_nombre_banco ||\n data.propuesta_nombre_banco ||\n actual.propuesta_nombre_banco ||\n mejorMatch.nombre ||\n ''\n);\n\nconst linkBancoOriginal = limpiar(\n match.propuesta_link_banco ||\n data.propuesta_link_banco ||\n actual.propuesta_link_banco ||\n mejorMatch.link ||\n ''\n);\n\nconst fileIdBanco = limpiar(\n match.propuesta_banco_file_id ||\n data.propuesta_banco_file_id ||\n actual.propuesta_banco_file_id ||\n mejorMatch.file_id ||\n ''\n);\n\nconst rowNumberBanco = limpiar(\n match.propuesta_banco_row_number ||\n data.propuesta_banco_row_number ||\n actual.propuesta_banco_row_number ||\n mejorMatch.row_number ||\n ''\n);\n\nconst enlacesExistentes = limpiar(\n match.propuesta_banco_enlaces_existentes ||\n data.propuesta_banco_enlaces_existentes ||\n actual.propuesta_banco_enlaces_existentes ||\n data.banco_enlaces_existentes ||\n actual.banco_enlaces_existentes ||\n mejorMatch.enlaces_ejecutadas_existentes ||\n ''\n);\n\nlet presentationUrl = limpiar(\n data.presentacion_ejecucion_url ||\n actual.presentacion_ejecucion_url ||\n data.presentation_url ||\n actual.presentation_url ||\n data.presentationUrl ||\n actual.presentationUrl ||\n data.webViewLink ||\n actual.webViewLink ||\n data.link_presentacion ||\n actual.link_presentacion ||\n ''\n);\n\nconst presentationId = limpiar(\n data.presentation_id ||\n actual.presentation_id ||\n ''\n);\n\nif (!presentationUrl && presentationId) {\n presentationUrl = `https://docs.google.com/presentation/d/${presentationId}/edit`;\n}\n\n// Fallback defensivo por si el estado quedó viejo.\nif (\n matchEstado !== 'MATCH_ALTA_CONFIANZA' &&\n matchConfianza >= 85\n) {\n matchEstado = 'MATCH_ALTA_CONFIANZA';\n}\n\nif (\n matchEstado !== 'MATCH_MEDIA_CONFIANZA' &&\n matchEstado !== 'MATCH_ALTA_CONFIANZA' &&\n matchConfianza >= 60 &&\n matchConfianza < 85\n) {\n matchEstado = 'MATCH_MEDIA_CONFIANZA';\n}\n\nif (!matchEstado || matchEstado === 'PENDIENTE') {\n if (matchConfianza >= 85) {\n matchEstado = 'MATCH_ALTA_CONFIANZA';\n } else if (matchConfianza >= 60) {\n matchEstado = 'MATCH_MEDIA_CONFIANZA';\n } else if (matchConfianza > 0) {\n matchEstado = 'MATCH_BAJA_CONFIANZA';\n }\n}\n\n// Formato limpio para el banco original:\n// Solo links, uno por línea.\nlet enlacesActualizados = enlacesExistentes;\n\nif (presentationUrl && !enlacesExistentes.includes(presentationUrl)) {\n enlacesActualizados = enlacesExistentes\n ? `${enlacesExistentes}\\n${presentationUrl}`\n : presentationUrl;\n}\n\nconst motivos = [];\n\nif (tipoReporte !== 'PROPUESTA_EJECUTADA') {\n motivos.push(`tipo_reporte es ${tipoReporte || 'NO_DETERMINADO'}`);\n}\n\nif (matchEstado !== 'MATCH_ALTA_CONFIANZA') {\n motivos.push(`propuesta_match_estado es ${matchEstado || 'SIN_MATCH'}`);\n}\n\nif (matchConfianza < 85) {\n motivos.push(`propuesta_match_confianza es ${matchConfianza}`);\n}\n\nif (!presentationUrl) {\n motivos.push('no existe presentacion_ejecucion_url');\n}\n\nif (!nombreBanco) {\n motivos.push('no llegó propuesta_nombre_banco');\n}\n\nif (!rowNumberBanco) {\n motivos.push('no llegó propuesta_banco_row_number');\n}\n\nif (presentationUrl && enlacesExistentes.includes(presentationUrl)) {\n motivos.push('el link ya existe en la columna de enlaces ejecutados');\n}\n\nconst actualizarBanco = motivos.length === 0;\n\nconst motivoNoActualizaBanco = actualizarBanco\n ? ''\n : `No se actualiza banco porque ${motivos.join('; ')}.`;\n\nreturn [\n {\n json: {\n ...data,\n\n actualizar_banco_original: actualizarBanco,\n motivo_no_actualiza_banco: motivoNoActualizaBanco,\n\n propuesta_match_estado: matchEstado || data.propuesta_match_estado || 'SIN_MATCH',\n propuesta_match_confianza: String(matchConfianza || 0),\n propuesta_nombre_banco: nombreBanco,\n propuesta_link_banco: linkBancoOriginal,\n propuesta_banco_file_id: fileIdBanco,\n propuesta_banco_row_number: rowNumberBanco,\n propuesta_banco_enlaces_existentes: enlacesExistentes,\n\n banco_match_column: 'row_number',\n banco_match_value: rowNumberBanco,\n\n row_number: rowNumberBanco,\n 'Enlaces a propuestas ejecutadas': enlacesActualizados,\n\n banco_enlaces_existentes: enlacesExistentes,\n banco_enlaces_actualizados: enlacesActualizados,\n banco_presentacion_url: presentationUrl,\n\n banco_update_debug: {\n tipo_reporte: tipoReporte,\n propuesta_match_estado: matchEstado,\n propuesta_match_confianza: matchConfianza,\n propuesta_nombre_banco: nombreBanco,\n propuesta_link_banco: linkBancoOriginal,\n propuesta_banco_file_id: fileIdBanco,\n propuesta_banco_row_number: rowNumberBanco,\n presentationUrl,\n enlacesExistentes,\n enlacesActualizados,\n actualizarBanco,\n motivoNoActualizaBanco,\n mejor_match: mejorMatch,\n match_debug: matchDebug\n }\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 6928, 1232 ], "id": "3dd8a8af-4902-4a0f-92b5-94e921262ae4", "name": "Code - Preparar actualización banco propuesta ejecutada TEST" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "dccbea1d-e9cf-4f51-b94d-7b0162737548", "leftValue": "={{ $json.actualizar_banco_original === true }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ 7696, 1280 ], "id": "92a6348b-03df-4edc-b1e7-994621c331b7", "name": "IF - Actualizar banco match alta confianza TEST" }, { "parameters": { "operation": "update", "documentId": { "__rl": true, "value": "1iy3lC3DS3ZfShb7d26iQOrPjx9s_3B-PC07XfYew7ng", "mode": "list", "cachedResultName": "BANCO DE PROPUESTAS DE CDC PARA FULGENCIO FUMADO", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iy3lC3DS3ZfShb7d26iQOrPjx9s_3B-PC07XfYew7ng/edit?usp=drivesdk" }, "sheetName": { "__rl": true, "value": "gid=0", "mode": "list", "cachedResultName": "propuestas", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iy3lC3DS3ZfShb7d26iQOrPjx9s_3B-PC07XfYew7ng/edit#gid=0" }, "columns": { "mappingMode": "defineBelow", "value": { "Enlaces a propuestas ejecutadas": "={{ $json.banco_enlaces_actualizados }}", "row_number": "={{ $json.propuesta_banco_row_number }}" }, "matchingColumns": [ "row_number" ], "schema": [ { "id": "NOMBRE", "displayName": "NOMBRE", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "TIPO DE ACCION", "displayName": "TIPO DE ACCION", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "CLIENTE", "displayName": "CLIENTE", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "MARCA", "displayName": "MARCA", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "PAIS", "displayName": "PAIS", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "CANAL", "displayName": "CANAL", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "AMBIENTE DE COMPRA (RE)", "displayName": "AMBIENTE DE COMPRA (RE)", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "TÁCTICA PROMOCIONAL", "displayName": "TÁCTICA PROMOCIONAL", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "APROBADA", "displayName": "APROBADA", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "ETIQUETAS", "displayName": "ETIQUETAS", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "AÑO", "displayName": "AÑO", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "Enlace a la propuesta", "displayName": "Enlace a la propuesta", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "Descripcion", "displayName": "Descripcion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "file_id", "displayName": "file_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "nombre_archivo", "displayName": "nombre_archivo", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "mime_type", "displayName": "mime_type", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "fuente_pais", "displayName": "fuente_pais", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "confianza_pais", "displayName": "confianza_pais", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "requiere_revision", "displayName": "requiere_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "procesado_ia", "displayName": "procesado_ia", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "ultima_actualizacion", "displayName": "ultima_actualizacion", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "motivos_revision", "displayName": "motivos_revision", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "Enlaces a propuestas ejecutadas", "displayName": "Enlaces a propuestas ejecutadas", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true }, { "id": "tablero_sync_key", "displayName": "tablero_sync_key", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "tablero_project_id", "displayName": "tablero_project_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "tablero_origen", "displayName": "tablero_origen", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "drive_modified_time", "displayName": "drive_modified_time", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "ultimo_refresco_ia", "displayName": "ultimo_refresco_ia", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "refresh_status", "displayName": "refresh_status", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "refresh_attempts", "displayName": "refresh_attempts", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "converted_google_slides_id", "displayName": "converted_google_slides_id", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "converted_google_slides_link", "displayName": "converted_google_slides_link", "required": false, "defaultMatch": false, "display": true, "type": "string", "canBeUsedToMatch": true, "removed": true }, { "id": "row_number", "displayName": "row_number", "required": false, "defaultMatch": false, "display": true, "type": "number", "canBeUsedToMatch": true, "readOnly": true, "removed": false } ], "attemptToConvertTypes": false, "convertFieldsToString": false }, "options": {} }, "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.7, "position": [ 8208, 1072 ], "id": "7c0dc504-1094-461d-82d6-469db24200b9", "name": "Sheets - Actualizar link ejecutada en banco TEST", "credentials": { "googleSheetsOAuth2Api": { "id": "K0hDZh3a85MpOHCs", "name": "Google Sheets account 2" } } }, { "parameters": { "jsCode": "const respuestaBanco = $json || {};\n\nconst base = $('Code - Preparar actualización banco propuesta ejecutada TEST').first().json || {};\n\nreturn [\n {\n json: {\n ...base,\n\n banco_original_actualizado: true,\n banco_original_response: respuestaBanco\n }\n }\n];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 8416, 1072 ], "id": "f9965d96-e94f-4a44-8604-b098fe9a08b9", "name": "Code - Restaurar contexto actualización banco TEST" } ], "connections": { "Code - Normalizar evento WhatsApp TEST": { "main": [ [ { "node": "Mi numero TESTING", "type": "main", "index": 0 } ] ] }, "Mi numero TESTING": { "main": [ [ { "node": "Sheets - Leer sesiones existentes", "type": "main", "index": 0 } ] ] }, "Webhook - Evolution WhatsApp TEST": { "main": [ [ { "node": "Code - Normalizar evento WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "WhatsApp - Enviar mensaje TEST": { "main": [ [] ] }, "Sheets - Leer sesiones existentes": { "main": [ [ { "node": "Code - Resolver sesión WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Resolver sesión WhatsApp TEST": { "main": [ [ { "node": "Switch - Decisión sesión WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Switch - Decisión sesión WhatsApp TEST": { "main": [ [ { "node": "Sheets - Crear sesión WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar aviso sesión activa WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar sin sesión para cancelar WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar cancelación sesión WhatsApp TEST", "type": "main", "index": 0 } ], [], [ { "node": "Code - Resolver paso activo WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar bienvenida WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Crear sesión WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar bienvenida WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Resolver paso activo WhatsApp TEST": { "main": [ [ { "node": "Switch - Paso activo WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Switch - Paso activo WhatsApp TEST": { "main": [ [ { "node": "Sheets - Guardar evento audio WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar solicitud audio WhatsApp TEST", "type": "main", "index": 0 } ], [], [], [ { "node": "Sheets - Guardar evento imagen WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Sheets - Leer eventos sesión fotos WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar aviso falta imagen WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Sheets - Guardar evento video WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "CERRAR_SIN_VIDEO", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar cierre con videos WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar recordatorio videos WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar aviso procesando WhatsApp TEST", "type": "main", "index": 0 } ], [], [] ] }, "Code - Preparar solicitud audio WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Guardar evento audio WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar actualización audio recibido WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar actualización audio recibido WhatsApp TEST": { "main": [ [ { "node": "Sheets - Actualizar sesión audio recibido WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar sesión audio recibido WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar solicitud imágenes WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar aviso sesión activa WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar solicitud imágenes WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar cancelación sesión WhatsApp TEST": { "main": [ [ { "node": "Sheets - Cancelar sesión WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Cancelar sesión WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar mensaje cancelación WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar mensaje cancelación WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Code — Preparar actualización imagen recibida": { "main": [ [ { "node": "Sheets — Actualizar sesión imagen recibida", "type": "main", "index": 0 } ] ] }, "Sheets — Actualizar sesión imagen recibida": { "main": [ [ { "node": "Code — Preparar confirmación imagen", "type": "main", "index": 0 } ] ] }, "Sheets - Guardar evento imagen WhatsApp TEST": { "main": [ [ { "node": "Code — Preparar actualización imagen recibida", "type": "main", "index": 0 } ] ] }, "Code - Preparar aviso falta imagen WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Code — Preparar confirmación imagen": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Preparar actualización fotos listas": { "main": [ [ { "node": "Sheets - Actualizar sesión fotos listas WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar sesión fotos listas WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar solicitud videos WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar solicitud videos WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar recordatorio videos WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "CERRAR_SIN_VIDEO": { "main": [ [ { "node": "Sheets - Actualizar sesión cierre sin video WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar sesión cierre sin video WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar aviso procesando WhatsApp TEST1", "type": "main", "index": 0 } ] ] }, "Sheets - Guardar evento video WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar actualización video recibido WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar actualización video recibido WhatsApp TEST": { "main": [ [ { "node": "Sheets - Actualizar sesión video recibido WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar sesión video recibido WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar confirmación video WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar confirmación video WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar cierre con videos WhatsApp TEST": { "main": [ [ { "node": "Sheets - Actualizar sesión cierre con videos WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar sesión cierre con videos WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar aviso procesando WhatsApp TEST1", "type": "main", "index": 0 } ] ] }, "Code - Preparar aviso procesando WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar búsqueda eventos media WhatsApp TEST": { "main": [ [ { "node": "Sheets - Leer eventos WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Leer eventos WhatsApp TEST": { "main": [ [ { "node": "Code - Filtrar eventos media de sesión WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Filtrar eventos media de sesión WhatsApp TEST": { "main": [ [ { "node": "Code - Separar media en items WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Separar media en items WhatsApp TEST": { "main": [ [ { "node": "HTTP Request - Obtener media base64 Evolution TEST", "type": "main", "index": 0 } ] ] }, "HTTP Request - Obtener media base64 Evolution TEST": { "main": [ [ { "node": "Code - Convertir base64 a binario WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Convertir base64 a binario WhatsApp TEST": { "main": [ [ { "node": "Drive - Subir media WhatsApp TEST", "type": "main", "index": 0 }, { "node": "Switch - Tipo media para Gemini WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Drive - Subir media WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar registro media Drive WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar registro media Drive WhatsApp TEST": { "main": [ [ { "node": "Sheets - Guardar media Drive WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Guardar media Drive WhatsApp TEST": { "main": [ [ { "node": "Code - Consolidar media recuperada WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Consolidar media recuperada WhatsApp TEST": { "main": [ [ { "node": "Sheets - Actualizar sesión media recuperada WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar sesión media recuperada WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar paquete análisis Gemini WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Switch - Tipo media para Gemini WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar Gemini audio WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar Gemini imagen WhatsApp TEST", "type": "main", "index": 0 } ], [ { "node": "Code - Preparar Gemini video WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar Gemini audio WhatsApp TEST": { "main": [ [ { "node": "Gemini - Analizar audio WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Gemini - Analizar audio WhatsApp TEST": { "main": [ [ { "node": "Code - Normalizar análisis audio WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar Gemini imagen WhatsApp TEST": { "main": [ [ { "node": "Gemini - Analizar imagen WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Gemini - Analizar imagen WhatsApp TEST": { "main": [ [ { "node": "Code - Normalizar análisis imagen WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar Gemini video WhatsApp TEST": { "main": [ [ { "node": "Analyze video", "type": "main", "index": 0 } ] ] }, "Analyze video": { "main": [ [ { "node": "Code - Normalizar análisis video WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Normalizar análisis video WhatsApp TEST": { "main": [ [ { "node": "Sheets - Guardar análisis media Gemini WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Normalizar análisis imagen WhatsApp TEST": { "main": [ [ { "node": "Sheets - Guardar análisis media Gemini WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Normalizar análisis audio WhatsApp TEST": { "main": [ [ { "node": "Sheets - Guardar análisis media Gemini WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Guardar análisis media Gemini WhatsApp TEST": { "main": [ [ { "node": "IF - Es último análisis media WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Leer análisis media Gemini WhatsApp TEST": { "main": [ [ { "node": "Code - Consolidar análisis media Gemini WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Consolidar análisis media Gemini WhatsApp TEST": { "main": [ [ { "node": "Gemini - Generar JSON final propuesta ejecutada TEST", "type": "main", "index": 0 } ] ] }, "Gemini - Generar JSON final propuesta ejecutada TEST": { "main": [ [ { "node": "Code - Normalizar JSON final propuesta ejecutada TEST", "type": "main", "index": 0 } ] ] }, "Code - Normalizar JSON final propuesta ejecutada TEST": { "main": [ [ { "node": "Sheets - Leer banco propuestas Fulgencio TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Guardar propuesta ejecutada final TEST": { "main": [ [ { "node": "Code - Preparar copia presentación ejecución TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar sesión análisis completado WhatsApp TEST": { "main": [ [ { "node": "Code - Preparar mensaje final WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar mensaje final WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "IF - Es último análisis media WhatsApp TEST": { "main": [ [ { "node": "Wait", "type": "main", "index": 0 } ] ] }, "Wait": { "main": [ [ { "node": "Sheets - Leer análisis media Gemini WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Leer propuestas ejecutadas final TEST": { "main": [ [ { "node": "Code - Verificar duplicado propuesta ejecutada TEST", "type": "main", "index": 0 } ] ] }, "Code - Verificar duplicado propuesta ejecutada TEST": { "main": [ [ { "node": "IF - Propuesta final ya existe TEST", "type": "main", "index": 0 } ] ] }, "IF - Propuesta final ya existe TEST": { "main": [ [], [ { "node": "Sheets - Guardar propuesta ejecutada final TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar copia presentación ejecución TEST": { "main": [ [ { "node": "Code - Preparar carpeta ejecución TEST", "type": "main", "index": 0 } ] ] }, "Drive - Copiar plantilla presentación ejecución TEST": { "main": [ [ { "node": "Code - Normalizar link presentación ejecución TEST", "type": "main", "index": 0 } ] ] }, "Code - Normalizar link presentación ejecución TEST": { "main": [ [ { "node": "Code - Preparar reemplazos Slides ejecución TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar link presentación ejecución TEST": { "main": [ [ { "node": "Code - Preparar actualización banco propuesta ejecutada TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar reemplazos Slides ejecución TEST": { "main": [ [ { "node": "HTTP Request - Reemplazar textos Slides ejecución TEST", "type": "main", "index": 0 } ] ] }, "HTTP Request - Reemplazar textos Slides ejecución TEST": { "main": [ [ { "node": "Code - Preparar payload insertar imágenes Slides TEST", "type": "main", "index": 0 } ] ] }, "Code - Normalizar respuesta Slides ejecución TEST": { "main": [ [ { "node": "Sheets - Actualizar link presentación ejecución TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar sin sesión para cancelar WhatsApp TEST": { "main": [ [ { "node": "WhatsApp - Enviar mensaje TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Leer eventos sesión fotos WhatsApp TEST": { "main": [ [ { "node": "Preparar actualización fotos listas", "type": "main", "index": 0 } ] ] }, "Code - Preparar carpeta ejecución TEST": { "main": [ [ { "node": "Drive - Crear carpeta ejecución TEST", "type": "main", "index": 0 } ] ] }, "Drive - Crear carpeta ejecución TEST": { "main": [ [ { "node": "Code - Normalizar carpeta ejecución TEST", "type": "main", "index": 0 } ] ] }, "Code - Normalizar carpeta ejecución TEST": { "main": [ [ { "node": "Code - Preparar media para mover carpeta TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar media para mover carpeta TEST": { "main": [ [ { "node": "Drive - Mover media a carpeta ejecución TEST", "type": "main", "index": 0 } ] ] }, "Drive - Mover media a carpeta ejecución TEST": { "main": [ [ { "node": "Code - Confirmar media movida carpeta TEST", "type": "main", "index": 0 } ] ] }, "Code - Confirmar media movida carpeta TEST": { "main": [ [ { "node": "Drive - Copiar plantilla presentación ejecución TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar payload insertar imágenes Slides TEST": { "main": [ [ { "node": "HTTP - Insertar imágenes en Slides TEST", "type": "main", "index": 0 } ] ] }, "HTTP - Insertar imágenes en Slides TEST": { "main": [ [ { "node": "Code - Normalizar respuesta Slides ejecución TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar aviso procesando WhatsApp TEST1": { "main": [ [ { "node": "HTTP - Enviar aviso procesando WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Code - Restaurar contexto aviso procesando TEST": { "main": [ [ { "node": "Code - Preparar búsqueda eventos media WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "HTTP - Enviar aviso procesando WhatsApp TEST": { "main": [ [ { "node": "Code - Restaurar contexto aviso procesando TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Leer banco propuestas Fulgencio TEST": { "main": [ [ { "node": "Code - Match propuesta banco Fulgencio TEST", "type": "main", "index": 0 } ] ] }, "Code - Match propuesta banco Fulgencio TEST": { "main": [ [ { "node": "Sheets - Leer propuestas ejecutadas final TEST", "type": "main", "index": 0 } ] ] }, "Code - Preparar actualización banco propuesta ejecutada TEST": { "main": [ [ { "node": "IF - Actualizar banco match alta confianza TEST", "type": "main", "index": 0 } ] ] }, "IF - Actualizar banco match alta confianza TEST": { "main": [ [ { "node": "Sheets - Actualizar link ejecutada en banco TEST", "type": "main", "index": 0 } ], [ { "node": "Sheets - Actualizar sesión análisis completado WhatsApp TEST", "type": "main", "index": 0 } ] ] }, "Sheets - Actualizar link ejecutada en banco TEST": { "main": [ [ { "node": "Code - Restaurar contexto actualización banco TEST", "type": "main", "index": 0 } ] ] }, "Code - Restaurar contexto actualización banco TEST": { "main": [ [ { "node": "Sheets - Actualizar sesión análisis completado WhatsApp TEST", "type": "main", "index": 0 } ] ] } }, "authors": "Isaac Aracena", "name": "Version 94d48ce3", "description": "", "autosaved": true, "workflowPublishHistory": [ { "createdAt": "2026-06-20T16:00:09.191Z", "id": 1680, "workflowId": "sXH75CXIQ0sEzm1Y", "versionId": "94d48ce3-27cf-4be2-8d30-467fb212e28b", "event": "activated", "userId": "0a88c0b1-928e-4412-896e-c5d1c99b2029" } ] } }