Update backup: Tablero CDC - Refrescar Banco Fulgencio Diario

This commit is contained in:
2026-08-02 04:00:48 +00:00
parent aae359276a
commit dc4aa782c2
+201 -59
View File
@@ -1,5 +1,5 @@
{ {
"updatedAt": "2026-07-25T17:10:33.663Z", "updatedAt": "2026-07-27T14:23:33.135Z",
"createdAt": "2026-06-16T17:22:11.395Z", "createdAt": "2026-06-16T17:22:11.395Z",
"id": "g2JtKXNz5GDbkmQH", "id": "g2JtKXNz5GDbkmQH",
"name": "Tablero CDC - Refrescar Banco Fulgencio Diario", "name": "Tablero CDC - Refrescar Banco Fulgencio Diario",
@@ -68,7 +68,7 @@
}, },
{ {
"parameters": { "parameters": {
"jsCode": "function clean(value) {\n return String(value ?? '').trim();\n}\n\nfunction extractGoogleFileId(url) {\n const text = clean(url);\n\n if (!text) return '';\n\n const patterns = [\n /\\/d\\/([a-zA-Z0-9_-]+)/,\n /id=([a-zA-Z0-9_-]+)/,\n /\\/file\\/d\\/([a-zA-Z0-9_-]+)/,\n /\\/presentation\\/d\\/([a-zA-Z0-9_-]+)/,\n ];\n\n for (const pattern of patterns) {\n const match = text.match(pattern);\n if (match && match[1]) return match[1];\n }\n\n return '';\n}\n\nconst rows = $input.all();\n\nconst result = [];\n\nfor (const item of rows) {\n const row = item.json || {};\n\n const origen = clean(row.tablero_origen);\n const syncKey = clean(row.tablero_sync_key);\n const projectId = clean(row.tablero_project_id);\n const proposalUrl = clean(row['Enlace a la propuesta']);\n const existingFileId = clean(row.file_id);\n const fileId = existingFileId || extractGoogleFileId(proposalUrl);\n\n if (origen !== 'Tablero CDC') continue;\n if (!syncKey) continue;\n if (!projectId) continue;\n if (!proposalUrl) continue;\n\n result.push({\n json: {\n ...row,\n file_id_refresco: fileId,\n proposal_url_refresco: proposalUrl,\n refresh_attempts_actual: Number(row.refresh_attempts || 0),\n },\n });\n}\n\nreturn items;" "jsCode": "function clean(value) {\n return String(value ?? '').trim();\n}\n\nfunction extractGoogleFileId(url) {\n const text = clean(url);\n\n if (!text) return '';\n\n const patterns = [\n /\\/d\\/([a-zA-Z0-9_-]+)/,\n /[?&]id=([a-zA-Z0-9_-]+)/,\n /\\/file\\/d\\/([a-zA-Z0-9_-]+)/,\n /\\/presentation\\/d\\/([a-zA-Z0-9_-]+)/,\n ];\n\n for (const pattern of patterns) {\n const match = text.match(pattern);\n\n if (match?.[1]) {\n return match[1];\n }\n }\n\n return '';\n}\n\nconst rows = $input.all();\nconst result = [];\n\nfor (let index = 0; index < rows.length; index++) {\n const item = rows[index];\n const row = item.json || {};\n\n const origen = clean(row.tablero_origen).toLowerCase();\n const syncKey = clean(row.tablero_sync_key);\n const projectId = clean(row.tablero_project_id);\n const proposalUrl = clean(row['Enlace a la propuesta']);\n\n const existingFileId = clean(row.file_id);\n const extractedFileId = extractGoogleFileId(proposalUrl);\n const fileId = existingFileId || extractedFileId;\n\n // Solo procesar filas válidas provenientes de Tablero CDC.\n if (origen !== 'tablero cdc') continue;\n if (!syncKey) continue;\n if (!projectId) continue;\n if (!proposalUrl) continue;\n\n result.push({\n json: {\n ...row,\n\n // Dejamos ambos campos consistentes.\n file_id: fileId,\n file_id_refresco: fileId,\n\n proposal_url_refresco: proposalUrl,\n refresh_attempts_actual: Number(row.refresh_attempts || 0),\n },\n\n // Conserva la vinculación entre el elemento original y el resultado.\n pairedItem: {\n item: index,\n },\n });\n}\n\nif (result.length === 0) {\n throw new Error(\n 'No se encontraron filas válidas de Tablero CDC con tablero_sync_key, tablero_project_id y enlace.'\n );\n}\n\nreturn result;"
}, },
"type": "n8n-nodes-base.code", "type": "n8n-nodes-base.code",
"typeVersion": 2, "typeVersion": 2,
@@ -104,7 +104,7 @@
"conditions": [ "conditions": [
{ {
"id": "8c79f0ce-b901-43c8-b863-ad490d267c37", "id": "8c79f0ce-b901-43c8-b863-ad490d267c37",
"leftValue": "={{ $json[\"file_id\"] }}", "leftValue": "={{ String($json.file_id_refresco || $json.file_id || '').trim() }}",
"rightValue": "", "rightValue": "",
"operator": { "operator": {
"type": "string", "type": "string",
@@ -152,7 +152,7 @@
}, },
{ {
"parameters": { "parameters": {
"jsCode": "const original = $('IF - Tiene file_id?').item.json || {};\nconst metadata = $json || {};\n\nfunction clean(value) {\n return String(value ?? '').trim();\n}\n\nfunction toDate(value) {\n const date = value ? new Date(value) : null;\n if (!date || Number.isNaN(date.getTime())) return null;\n return date;\n}\n\nconst driveModifiedTime = clean(metadata.modifiedTime || metadata.modified_time);\nconst lastRefresh = clean(original.ultimo_refresco_ia);\nconst existingDriveModified = clean(original.drive_modified_time);\n\nconst driveDate = toDate(driveModifiedTime);\nconst lastRefreshDate = toDate(lastRefresh);\nconst existingDriveDate = toDate(existingDriveModified);\n\nconst driveSizeBytes = Number(metadata.size || 0);\nconst driveSizeMb = driveSizeBytes ? Number((driveSizeBytes / 1024 / 1024).toFixed(2)) : 0;\n\nlet shouldRefresh = false;\nlet reason = '';\n\nif (!driveDate) {\n shouldRefresh = false;\n reason = 'sin_modified_time';\n} else if (!lastRefreshDate) {\n shouldRefresh = true;\n reason = 'nunca_refrescado';\n} else if (driveDate > lastRefreshDate) {\n shouldRefresh = true;\n reason = 'archivo_modificado_desde_ultimo_refresco';\n} else if (existingDriveDate && driveDate > existingDriveDate) {\n shouldRefresh = true;\n reason = 'drive_modified_time_cambio';\n} else {\n shouldRefresh = false;\n reason = 'sin_cambios';\n}\n\nreturn [\n {\n json: {\n ...original,\n drive_file_id: metadata.id || original.file_id_refresco,\n drive_name: metadata.name || '',\n drive_mime_type: metadata.mimeType || '',\n drive_modified_time_actual: driveModifiedTime,\n drive_size_bytes: driveSizeBytes,\n drive_size_mb: driveSizeMb,\n should_refresh: shouldRefresh,\n refresh_reason: reason,\n },\n },\n];" "jsCode": "const original = $('IF - Tiene file_id?').item.json || {};\nconst metadata = $json || {};\n\nfunction clean(value) {\n return String(value ?? '').trim();\n}\n\nfunction toDate(value) {\n const date = value ? new Date(value) : null;\n\n if (!date || Number.isNaN(date.getTime())) {\n return null;\n }\n\n return date;\n}\n\nconst driveModifiedTime = clean(\n metadata.modifiedTime || metadata.modified_time\n);\n\nconst lastRefresh = clean(original.ultimo_refresco_ia);\nconst existingDriveModified = clean(original.drive_modified_time);\nconst previousStatus = clean(original.refresh_status).toUpperCase();\n\nconst driveDate = toDate(driveModifiedTime);\nconst lastRefreshDate = toDate(lastRefresh);\nconst existingDriveDate = toDate(existingDriveModified);\n\nconst driveSizeBytes = Number(metadata.size || 0);\n\nconst driveSizeMb = driveSizeBytes\n ? Number((driveSizeBytes / 1024 / 1024).toFixed(2))\n : 0;\n\n// Estados que deben volver a intentarse aunque una ejecución anterior\n// haya escrito incorrectamente ultimo_refresco_ia.\nconst retryableStatuses = new Set([\n 'PENDIENTE',\n 'SIN_FILE_ID',\n 'ERROR_METADATA_DRIVE',\n 'ERROR_EXPORT_SLIDES',\n 'ERROR_CONVERT_PPTX',\n 'SIN_CONTENIDO',\n 'PPTX_DEMASIADO_PESADO',\n]);\n\nlet shouldRefresh = false;\nlet reason = '';\n\nif (retryableStatuses.has(previousStatus)) {\n shouldRefresh = true;\n reason = `reintento_estado_${previousStatus.toLowerCase()}`;\n} else if (!driveDate) {\n shouldRefresh = false;\n reason = 'sin_modified_time';\n} else if (!lastRefreshDate) {\n shouldRefresh = true;\n reason = 'nunca_refrescado';\n} else if (driveDate > lastRefreshDate) {\n shouldRefresh = true;\n reason = 'archivo_modificado_desde_ultimo_refresco';\n} else if (existingDriveDate && driveDate > existingDriveDate) {\n shouldRefresh = true;\n reason = 'drive_modified_time_cambio';\n} else {\n shouldRefresh = false;\n reason = 'sin_cambios';\n}\n\nreturn [\n {\n json: {\n ...original,\n\n drive_file_id:\n metadata.id ||\n original.file_id_refresco ||\n original.file_id ||\n '',\n\n drive_name: metadata.name || '',\n drive_mime_type: metadata.mimeType || '',\n drive_modified_time_actual: driveModifiedTime,\n drive_size_bytes: driveSizeBytes,\n drive_size_mb: driveSizeMb,\n\n should_refresh: shouldRefresh,\n refresh_reason: reason,\n },\n },\n];"
}, },
"type": "n8n-nodes-base.code", "type": "n8n-nodes-base.code",
"typeVersion": 2, "typeVersion": 2,
@@ -371,7 +371,7 @@
"columns": { "columns": {
"mappingMode": "defineBelow", "mappingMode": "defineBelow",
"value": { "value": {
"tablero_sync_key": "={{ $json[\"tablero_sync_key\"]}}", "tablero_sync_key": "={{ String($json.tablero_sync_key || '').trim() }}",
"NOMBRE": "={{ $json[\"NOMBRE\"]}}", "NOMBRE": "={{ $json[\"NOMBRE\"]}}",
"TIPO DE ACCION": "={{ $json[\"TIPO DE ACCION\"]}}", "TIPO DE ACCION": "={{ $json[\"TIPO DE ACCION\"]}}",
"CLIENTE": "={{ $json[\"CLIENTE\"]}}", "CLIENTE": "={{ $json[\"CLIENTE\"]}}",
@@ -714,6 +714,8 @@
], ],
"id": "f6487012-88de-4be3-b62e-fa9bdfdda270", "id": "f6487012-88de-4be3-b62e-fa9bdfdda270",
"name": "Google Sheets - Upsert Banco Fulgencio", "name": "Google Sheets - Upsert Banco Fulgencio",
"retryOnFail": true,
"maxTries": 5,
"credentials": { "credentials": {
"googleSheetsOAuth2Api": { "googleSheetsOAuth2Api": {
"id": "K0hDZh3a85MpOHCs", "id": "K0hDZh3a85MpOHCs",
@@ -723,7 +725,7 @@
}, },
{ {
"parameters": { "parameters": {
"jsCode": "const original = $('Code - Preparar texto IA Refresco').item.json || {};\n\nreturn [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time_actual || original.drive_modified_time || '',\n ultimo_refresco_ia: new Date().toISOString(),\n refresh_status: 'SIN_CONTENIDO',\n refresh_attempts: original.refresh_attempts || 1,\n procesado_ia: false,\n\n converted_google_slides_id: original.converted_google_slides_id || '',\n converted_google_slides_link: original.converted_google_slides_link || '',\n },\n },\n];" "jsCode": "const original = $('Code - Preparar texto IA Refresco').item.json || {};\n\nreturn [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time_actual || original.drive_modified_time || '',\n ultimo_refresco_ia: original.ultimo_refresco_ia || '',\n ultima_actualizacion: new Date().toISOString(),\n refresh_status: 'SIN_CONTENIDO',\n refresh_attempts: original.refresh_attempts || 1,\n procesado_ia: false,\n\n converted_google_slides_id: original.converted_google_slides_id || '',\n converted_google_slides_link: original.converted_google_slides_link || '',\n },\n },\n];"
}, },
"type": "n8n-nodes-base.code", "type": "n8n-nodes-base.code",
"typeVersion": 2, "typeVersion": 2,
@@ -749,7 +751,7 @@
}, },
{ {
"parameters": { "parameters": {
"jsCode": "const original = $('IF - Tiene file_id?').item.json || {};\nconst errorData = $json || {};\n\nfunction stringifyError(value) {\n try {\n if (typeof value === 'string') return value;\n return JSON.stringify(value).slice(0, 500);\n } catch (error) {\n return String(value).slice(0, 500);\n }\n}\n\nreturn [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time || '',\n ultimo_refresco_ia: new Date().toISOString(),\n refresh_status: 'ERROR_METADATA_DRIVE',\n refresh_attempts: Number(original.refresh_attempts_actual || original.refresh_attempts || 0) + 1,\n procesado_ia: false,\n motivos_revision: `error_metadata_drive: ${stringifyError(errorData)}`,\n\n converted_google_slides_id: original.converted_google_slides_id || '',\n converted_google_slides_link: original.converted_google_slides_link || '',\n },\n },\n];" "jsCode": "const original = $('IF - Tiene file_id?').item.json || {};\nconst errorData = $json || {};\n\nfunction stringifyError(value) {\n try {\n if (typeof value === 'string') return value;\n return JSON.stringify(value).slice(0, 500);\n } catch (error) {\n return String(value).slice(0, 500);\n }\n}\n\nreturn [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time || '',\n ultimo_refresco_ia: original.ultimo_refresco_ia || '',\n ultima_actualizacion: new Date().toISOString(),\n refresh_status: 'ERROR_METADATA_DRIVE',\n refresh_attempts: Number(original.refresh_attempts_actual || original.refresh_attempts || 0) + 1,\n procesado_ia: false,\n motivos_revision: `error_metadata_drive: ${stringifyError(errorData)}`,\n\n converted_google_slides_id: original.converted_google_slides_id || '',\n converted_google_slides_link: original.converted_google_slides_link || '',\n },\n },\n];"
}, },
"type": "n8n-nodes-base.code", "type": "n8n-nodes-base.code",
"typeVersion": 2, "typeVersion": 2,
@@ -762,7 +764,7 @@
}, },
{ {
"parameters": { "parameters": {
"jsCode": "function getNodeJson(name) {\n try {\n return $(name).item.json || {};\n } catch (error) {\n return {};\n }\n}\n\nconst evaluated = getNodeJson('Code - Evaluar refresco');\nconst converted = getNodeJson('Code - Preparar ID convertido');\n\nconst original = Object.keys(converted).length ? converted : evaluated;\nconst errorData = $json || {};\n\nfunction stringifyError(value) {\n try {\n if (typeof value === 'string') return value;\n return JSON.stringify(value).slice(0, 500);\n } catch (error) {\n return String(value).slice(0, 500);\n }\n}\n\nreturn [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time_actual || original.drive_modified_time || '',\n ultimo_refresco_ia: new Date().toISOString(),\n refresh_status: 'ERROR_EXPORT_SLIDES',\n refresh_attempts: Number(original.refresh_attempts_actual || original.refresh_attempts || 0) + 1,\n procesado_ia: false,\n motivos_revision: `error_export_slides: ${stringifyError(errorData)}`,\n\n converted_google_slides_id: original.converted_google_slides_id || '',\n converted_google_slides_link: original.converted_google_slides_link || '',\n },\n },\n];" "jsCode": "function getNodeJson(name) {\n try {\n return $(name).item.json || {};\n } catch (error) {\n return {};\n }\n}\n\nconst evaluated = getNodeJson('Code - Evaluar refresco');\nconst converted = getNodeJson('Code - Preparar ID convertido');\n\nconst original = Object.keys(converted).length ? converted : evaluated;\nconst errorData = $json || {};\n\nfunction stringifyError(value) {\n try {\n if (typeof value === 'string') return value;\n return JSON.stringify(value).slice(0, 500);\n } catch (error) {\n return String(value).slice(0, 500);\n }\n}\n\nreturn [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time_actual || original.drive_modified_time || '',\n ultimo_refresco_ia: original.ultimo_refresco_ia || '',\n ultima_actualizacion: new Date().toISOString(),\n refresh_status: 'ERROR_EXPORT_SLIDES',\n refresh_attempts: Number(original.refresh_attempts_actual || original.refresh_attempts || 0) + 1,\n procesado_ia: false,\n motivos_revision: `error_export_slides: ${stringifyError(errorData)}`,\n\n converted_google_slides_id: original.converted_google_slides_id || '',\n converted_google_slides_link: original.converted_google_slides_link || '',\n },\n },\n];"
}, },
"type": "n8n-nodes-base.code", "type": "n8n-nodes-base.code",
"typeVersion": 2, "typeVersion": 2,
@@ -849,7 +851,7 @@
}, },
{ {
"parameters": { "parameters": {
"jsCode": "const original = $('IF - Es Google Slides nativo?').item.json || {};\nconst response = $json || {};\n\nfunction clean(value) {\n return String(value ?? '').trim();\n}\n\nif (!response.ok || !response.converted_google_slides_id) {\n return [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time_actual || original.drive_modified_time || '',\n ultimo_refresco_ia: new Date().toISOString(),\n refresh_status: 'ERROR_CONVERT_PPTX',\n refresh_attempts: Number(original.refresh_attempts_actual || original.refresh_attempts || 0) + 1,\n procesado_ia: false,\n motivos_revision: `error_convert_pptx: ${clean(response.error || JSON.stringify(response)).slice(0, 300)}`,\n },\n },\n ];\n}\n\nreturn [\n {\n json: {\n ...original,\n\n drive_file_id_original: original.drive_file_id || original.file_id_refresco || '',\n drive_file_id: response.converted_google_slides_id,\n drive_mime_type: 'application/vnd.google-apps.presentation',\n\n converted_google_slides_id: response.converted_google_slides_id,\n converted_google_slides_link: response.converted_google_slides_link,\n\n refresh_status: 'CONVERTIDO_PPTX',\n },\n },\n];" "jsCode": "const original = $('IF - Es Google Slides nativo?').item.json || {};\nconst response = $json || {};\n\nfunction clean(value) {\n return String(value ?? '').trim();\n}\n\nif (!response.ok || !response.converted_google_slides_id) {\n return [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time_actual || original.drive_modified_time || '',\n ultimo_refresco_ia: original.ultimo_refresco_ia || '',\n ultima_actualizacion: new Date().toISOString(),\n refresh_status: 'ERROR_CONVERT_PPTX',\n refresh_attempts: Number(original.refresh_attempts_actual || original.refresh_attempts || 0) + 1,\n procesado_ia: false,\n motivos_revision: `error_convert_pptx: ${clean(response.error || JSON.stringify(response)).slice(0, 300)}`,\n },\n },\n ];\n}\n\nreturn [\n {\n json: {\n ...original,\n\n drive_file_id_original: original.drive_file_id || original.file_id_refresco || '',\n drive_file_id: response.converted_google_slides_id,\n drive_mime_type: 'application/vnd.google-apps.presentation',\n\n converted_google_slides_id: response.converted_google_slides_id,\n converted_google_slides_link: response.converted_google_slides_link,\n\n refresh_status: 'CONVERTIDO_PPTX',\n },\n },\n];"
}, },
"type": "n8n-nodes-base.code", "type": "n8n-nodes-base.code",
"typeVersion": 2, "typeVersion": 2,
@@ -928,7 +930,7 @@
}, },
{ {
"parameters": { "parameters": {
"jsCode": "const original = $json || {};\n\nreturn [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time_actual || original.drive_modified_time || '',\n ultimo_refresco_ia: new Date().toISOString(),\n refresh_status: 'PPTX_DEMASIADO_PESADO',\n refresh_attempts: Number(original.refresh_attempts_actual || original.refresh_attempts || 0) + 1,\n procesado_ia: false,\n motivos_revision: `pptx_demasiado_pesado_para_conversion_google_slides: ${original.drive_size_mb || 0} MB. Google Slides solo permite convertir presentaciones hasta 100 MB.`,\n\n converted_google_slides_id: original.converted_google_slides_id || '',\n converted_google_slides_link: original.converted_google_slides_link || '',\n },\n },\n];" "jsCode": "const original = $json || {};\n\nreturn [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time_actual || original.drive_modified_time || '',\n ultimo_refresco_ia: original.ultimo_refresco_ia || '',\n ultima_actualizacion: new Date().toISOString(),\n refresh_status: 'PPTX_DEMASIADO_PESADO',\n refresh_attempts: Number(original.refresh_attempts_actual || original.refresh_attempts || 0) + 1,\n procesado_ia: false,\n motivos_revision: `pptx_demasiado_pesado_para_conversion_google_slides: ${original.drive_size_mb || 0} MB. Google Slides solo permite convertir presentaciones hasta 100 MB.`,\n\n converted_google_slides_id: original.converted_google_slides_id || '',\n converted_google_slides_link: original.converted_google_slides_link || '',\n },\n },\n];"
}, },
"type": "n8n-nodes-base.code", "type": "n8n-nodes-base.code",
"typeVersion": 2, "typeVersion": 2,
@@ -1002,6 +1004,54 @@
], ],
"id": "fc72547c-317e-4ac7-9428-825e8504f143", "id": "fc72547c-317e-4ac7-9428-825e8504f143",
"name": "Sticky Note3" "name": "Sticky Note3"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "cb44c645-00ba-43c1-a8f0-2fe556ebc382",
"leftValue": "={{ String($json.tablero_sync_key || '').trim() }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
5024,
112
],
"id": "1b56b546-c4af-412d-bef8-8105eb830ae1",
"name": "IF - Tiene sync key antes de Upsert?"
},
{
"parameters": {
"amount": 4
},
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [
2768,
432
],
"id": "be090d5c-6fb6-4087-b25b-83fb8e3a3392",
"name": "Wait - Respetar cuota Google Sheets",
"webhookId": "8b997d7c-097c-4c8a-8793-6bce3db5e142"
} }
], ],
"connections": { "connections": {
@@ -1188,7 +1238,7 @@
"main": [ "main": [
[ [
{ {
"node": "Google Sheets - Upsert Banco Fulgencio", "node": "IF - Tiene sync key antes de Upsert?",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -1199,7 +1249,7 @@
"main": [ "main": [
[ [
{ {
"node": "Google Sheets - Upsert Banco Fulgencio", "node": "IF - Tiene sync key antes de Upsert?",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -1210,7 +1260,7 @@
"main": [ "main": [
[ [
{ {
"node": "Loop - Filas Banco Fulgencio", "node": "Wait - Respetar cuota Google Sheets",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -1221,7 +1271,7 @@
"main": [ "main": [
[ [
{ {
"node": "Google Sheets - Upsert Banco Fulgencio", "node": "IF - Tiene sync key antes de Upsert?",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -1232,7 +1282,7 @@
"main": [ "main": [
[ [
{ {
"node": "Google Sheets - Upsert Banco Fulgencio", "node": "IF - Tiene sync key antes de Upsert?",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -1243,7 +1293,7 @@
"main": [ "main": [
[ [
{ {
"node": "Google Sheets - Upsert Banco Fulgencio", "node": "IF - Tiene sync key antes de Upsert?",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -1301,7 +1351,7 @@
], ],
[ [
{ {
"node": "Google Sheets - Upsert Banco Fulgencio", "node": "IF - Tiene sync key antes de Upsert?",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -1327,6 +1377,17 @@
] ]
}, },
"Code - Marcar PPTX demasiado pesado": { "Code - Marcar PPTX demasiado pesado": {
"main": [
[
{
"node": "IF - Tiene sync key antes de Upsert?",
"type": "main",
"index": 0
}
]
]
},
"IF - Tiene sync key antes de Upsert?": {
"main": [ "main": [
[ [
{ {
@@ -1334,6 +1395,24 @@
"type": "main", "type": "main",
"index": 0 "index": 0
} }
],
[
{
"node": "Loop - Filas Banco Fulgencio",
"type": "main",
"index": 0
}
]
]
},
"Wait - Respetar cuota Google Sheets": {
"main": [
[
{
"node": "Loop - Filas Banco Fulgencio",
"type": "main",
"index": 0
}
] ]
] ]
} }
@@ -1355,9 +1434,9 @@
"meta": { "meta": {
"templateCredsSetupCompleted": true "templateCredsSetupCompleted": true
}, },
"versionId": "a92fd2c1-b300-4def-8cd7-fdb0bde1bdfe", "versionId": "db8caedf-99e0-4c41-b43a-4b115621fb4a",
"activeVersionId": "a92fd2c1-b300-4def-8cd7-fdb0bde1bdfe", "activeVersionId": "db8caedf-99e0-4c41-b43a-4b115621fb4a",
"versionCounter": 435, "versionCounter": 499,
"triggerCount": 1, "triggerCount": 1,
"shared": [ "shared": [
{ {
@@ -1380,9 +1459,9 @@
], ],
"tags": [], "tags": [],
"activeVersion": { "activeVersion": {
"updatedAt": "2026-07-22T14:43:58.000Z", "updatedAt": "2026-07-27T14:23:46.000Z",
"createdAt": "2026-07-22T14:43:55.809Z", "createdAt": "2026-07-27T14:23:33.139Z",
"versionId": "a92fd2c1-b300-4def-8cd7-fdb0bde1bdfe", "versionId": "db8caedf-99e0-4c41-b43a-4b115621fb4a",
"workflowId": "g2JtKXNz5GDbkmQH", "workflowId": "g2JtKXNz5GDbkmQH",
"nodes": [ "nodes": [
{ {
@@ -1446,7 +1525,7 @@
}, },
{ {
"parameters": { "parameters": {
"jsCode": "function clean(value) {\n return String(value ?? '').trim();\n}\n\nfunction extractGoogleFileId(url) {\n const text = clean(url);\n\n if (!text) return '';\n\n const patterns = [\n /\\/d\\/([a-zA-Z0-9_-]+)/,\n /id=([a-zA-Z0-9_-]+)/,\n /\\/file\\/d\\/([a-zA-Z0-9_-]+)/,\n /\\/presentation\\/d\\/([a-zA-Z0-9_-]+)/,\n ];\n\n for (const pattern of patterns) {\n const match = text.match(pattern);\n if (match && match[1]) return match[1];\n }\n\n return '';\n}\n\nconst rows = $input.all();\n\nconst result = [];\n\nfor (const item of rows) {\n const row = item.json || {};\n\n const origen = clean(row.tablero_origen);\n const syncKey = clean(row.tablero_sync_key);\n const projectId = clean(row.tablero_project_id);\n const proposalUrl = clean(row['Enlace a la propuesta']);\n const existingFileId = clean(row.file_id);\n const fileId = existingFileId || extractGoogleFileId(proposalUrl);\n\n if (origen !== 'Tablero CDC') continue;\n if (!syncKey) continue;\n if (!projectId) continue;\n if (!proposalUrl) continue;\n\n result.push({\n json: {\n ...row,\n file_id_refresco: fileId,\n proposal_url_refresco: proposalUrl,\n refresh_attempts_actual: Number(row.refresh_attempts || 0),\n },\n });\n}\n\nreturn items;" "jsCode": "function clean(value) {\n return String(value ?? '').trim();\n}\n\nfunction extractGoogleFileId(url) {\n const text = clean(url);\n\n if (!text) return '';\n\n const patterns = [\n /\\/d\\/([a-zA-Z0-9_-]+)/,\n /[?&]id=([a-zA-Z0-9_-]+)/,\n /\\/file\\/d\\/([a-zA-Z0-9_-]+)/,\n /\\/presentation\\/d\\/([a-zA-Z0-9_-]+)/,\n ];\n\n for (const pattern of patterns) {\n const match = text.match(pattern);\n\n if (match?.[1]) {\n return match[1];\n }\n }\n\n return '';\n}\n\nconst rows = $input.all();\nconst result = [];\n\nfor (let index = 0; index < rows.length; index++) {\n const item = rows[index];\n const row = item.json || {};\n\n const origen = clean(row.tablero_origen).toLowerCase();\n const syncKey = clean(row.tablero_sync_key);\n const projectId = clean(row.tablero_project_id);\n const proposalUrl = clean(row['Enlace a la propuesta']);\n\n const existingFileId = clean(row.file_id);\n const extractedFileId = extractGoogleFileId(proposalUrl);\n const fileId = existingFileId || extractedFileId;\n\n // Solo procesar filas válidas provenientes de Tablero CDC.\n if (origen !== 'tablero cdc') continue;\n if (!syncKey) continue;\n if (!projectId) continue;\n if (!proposalUrl) continue;\n\n result.push({\n json: {\n ...row,\n\n // Dejamos ambos campos consistentes.\n file_id: fileId,\n file_id_refresco: fileId,\n\n proposal_url_refresco: proposalUrl,\n refresh_attempts_actual: Number(row.refresh_attempts || 0),\n },\n\n // Conserva la vinculación entre el elemento original y el resultado.\n pairedItem: {\n item: index,\n },\n });\n}\n\nif (result.length === 0) {\n throw new Error(\n 'No se encontraron filas válidas de Tablero CDC con tablero_sync_key, tablero_project_id y enlace.'\n );\n}\n\nreturn result;"
}, },
"type": "n8n-nodes-base.code", "type": "n8n-nodes-base.code",
"typeVersion": 2, "typeVersion": 2,
@@ -1482,7 +1561,7 @@
"conditions": [ "conditions": [
{ {
"id": "8c79f0ce-b901-43c8-b863-ad490d267c37", "id": "8c79f0ce-b901-43c8-b863-ad490d267c37",
"leftValue": "={{ $json[\"file_id\"] }}", "leftValue": "={{ String($json.file_id_refresco || $json.file_id || '').trim() }}",
"rightValue": "", "rightValue": "",
"operator": { "operator": {
"type": "string", "type": "string",
@@ -1530,7 +1609,7 @@
}, },
{ {
"parameters": { "parameters": {
"jsCode": "const original = $('IF - Tiene file_id?').item.json || {};\nconst metadata = $json || {};\n\nfunction clean(value) {\n return String(value ?? '').trim();\n}\n\nfunction toDate(value) {\n const date = value ? new Date(value) : null;\n if (!date || Number.isNaN(date.getTime())) return null;\n return date;\n}\n\nconst driveModifiedTime = clean(metadata.modifiedTime || metadata.modified_time);\nconst lastRefresh = clean(original.ultimo_refresco_ia);\nconst existingDriveModified = clean(original.drive_modified_time);\n\nconst driveDate = toDate(driveModifiedTime);\nconst lastRefreshDate = toDate(lastRefresh);\nconst existingDriveDate = toDate(existingDriveModified);\n\nconst driveSizeBytes = Number(metadata.size || 0);\nconst driveSizeMb = driveSizeBytes ? Number((driveSizeBytes / 1024 / 1024).toFixed(2)) : 0;\n\nlet shouldRefresh = false;\nlet reason = '';\n\nif (!driveDate) {\n shouldRefresh = false;\n reason = 'sin_modified_time';\n} else if (!lastRefreshDate) {\n shouldRefresh = true;\n reason = 'nunca_refrescado';\n} else if (driveDate > lastRefreshDate) {\n shouldRefresh = true;\n reason = 'archivo_modificado_desde_ultimo_refresco';\n} else if (existingDriveDate && driveDate > existingDriveDate) {\n shouldRefresh = true;\n reason = 'drive_modified_time_cambio';\n} else {\n shouldRefresh = false;\n reason = 'sin_cambios';\n}\n\nreturn [\n {\n json: {\n ...original,\n drive_file_id: metadata.id || original.file_id_refresco,\n drive_name: metadata.name || '',\n drive_mime_type: metadata.mimeType || '',\n drive_modified_time_actual: driveModifiedTime,\n drive_size_bytes: driveSizeBytes,\n drive_size_mb: driveSizeMb,\n should_refresh: shouldRefresh,\n refresh_reason: reason,\n },\n },\n];" "jsCode": "const original = $('IF - Tiene file_id?').item.json || {};\nconst metadata = $json || {};\n\nfunction clean(value) {\n return String(value ?? '').trim();\n}\n\nfunction toDate(value) {\n const date = value ? new Date(value) : null;\n\n if (!date || Number.isNaN(date.getTime())) {\n return null;\n }\n\n return date;\n}\n\nconst driveModifiedTime = clean(\n metadata.modifiedTime || metadata.modified_time\n);\n\nconst lastRefresh = clean(original.ultimo_refresco_ia);\nconst existingDriveModified = clean(original.drive_modified_time);\nconst previousStatus = clean(original.refresh_status).toUpperCase();\n\nconst driveDate = toDate(driveModifiedTime);\nconst lastRefreshDate = toDate(lastRefresh);\nconst existingDriveDate = toDate(existingDriveModified);\n\nconst driveSizeBytes = Number(metadata.size || 0);\n\nconst driveSizeMb = driveSizeBytes\n ? Number((driveSizeBytes / 1024 / 1024).toFixed(2))\n : 0;\n\n// Estados que deben volver a intentarse aunque una ejecución anterior\n// haya escrito incorrectamente ultimo_refresco_ia.\nconst retryableStatuses = new Set([\n 'PENDIENTE',\n 'SIN_FILE_ID',\n 'ERROR_METADATA_DRIVE',\n 'ERROR_EXPORT_SLIDES',\n 'ERROR_CONVERT_PPTX',\n 'SIN_CONTENIDO',\n 'PPTX_DEMASIADO_PESADO',\n]);\n\nlet shouldRefresh = false;\nlet reason = '';\n\nif (retryableStatuses.has(previousStatus)) {\n shouldRefresh = true;\n reason = `reintento_estado_${previousStatus.toLowerCase()}`;\n} else if (!driveDate) {\n shouldRefresh = false;\n reason = 'sin_modified_time';\n} else if (!lastRefreshDate) {\n shouldRefresh = true;\n reason = 'nunca_refrescado';\n} else if (driveDate > lastRefreshDate) {\n shouldRefresh = true;\n reason = 'archivo_modificado_desde_ultimo_refresco';\n} else if (existingDriveDate && driveDate > existingDriveDate) {\n shouldRefresh = true;\n reason = 'drive_modified_time_cambio';\n} else {\n shouldRefresh = false;\n reason = 'sin_cambios';\n}\n\nreturn [\n {\n json: {\n ...original,\n\n drive_file_id:\n metadata.id ||\n original.file_id_refresco ||\n original.file_id ||\n '',\n\n drive_name: metadata.name || '',\n drive_mime_type: metadata.mimeType || '',\n drive_modified_time_actual: driveModifiedTime,\n drive_size_bytes: driveSizeBytes,\n drive_size_mb: driveSizeMb,\n\n should_refresh: shouldRefresh,\n refresh_reason: reason,\n },\n },\n];"
}, },
"type": "n8n-nodes-base.code", "type": "n8n-nodes-base.code",
"typeVersion": 2, "typeVersion": 2,
@@ -1749,7 +1828,7 @@
"columns": { "columns": {
"mappingMode": "defineBelow", "mappingMode": "defineBelow",
"value": { "value": {
"tablero_sync_key": "={{ $json[\"tablero_sync_key\"]}}", "tablero_sync_key": "={{ String($json.tablero_sync_key || '').trim() }}",
"NOMBRE": "={{ $json[\"NOMBRE\"]}}", "NOMBRE": "={{ $json[\"NOMBRE\"]}}",
"TIPO DE ACCION": "={{ $json[\"TIPO DE ACCION\"]}}", "TIPO DE ACCION": "={{ $json[\"TIPO DE ACCION\"]}}",
"CLIENTE": "={{ $json[\"CLIENTE\"]}}", "CLIENTE": "={{ $json[\"CLIENTE\"]}}",
@@ -2092,6 +2171,8 @@
], ],
"id": "f6487012-88de-4be3-b62e-fa9bdfdda270", "id": "f6487012-88de-4be3-b62e-fa9bdfdda270",
"name": "Google Sheets - Upsert Banco Fulgencio", "name": "Google Sheets - Upsert Banco Fulgencio",
"retryOnFail": true,
"maxTries": 5,
"credentials": { "credentials": {
"googleSheetsOAuth2Api": { "googleSheetsOAuth2Api": {
"id": "K0hDZh3a85MpOHCs", "id": "K0hDZh3a85MpOHCs",
@@ -2101,7 +2182,7 @@
}, },
{ {
"parameters": { "parameters": {
"jsCode": "const original = $('Code - Preparar texto IA Refresco').item.json || {};\n\nreturn [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time_actual || original.drive_modified_time || '',\n ultimo_refresco_ia: new Date().toISOString(),\n refresh_status: 'SIN_CONTENIDO',\n refresh_attempts: original.refresh_attempts || 1,\n procesado_ia: false,\n\n converted_google_slides_id: original.converted_google_slides_id || '',\n converted_google_slides_link: original.converted_google_slides_link || '',\n },\n },\n];" "jsCode": "const original = $('Code - Preparar texto IA Refresco').item.json || {};\n\nreturn [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time_actual || original.drive_modified_time || '',\n ultimo_refresco_ia: original.ultimo_refresco_ia || '',\n ultima_actualizacion: new Date().toISOString(),\n refresh_status: 'SIN_CONTENIDO',\n refresh_attempts: original.refresh_attempts || 1,\n procesado_ia: false,\n\n converted_google_slides_id: original.converted_google_slides_id || '',\n converted_google_slides_link: original.converted_google_slides_link || '',\n },\n },\n];"
}, },
"type": "n8n-nodes-base.code", "type": "n8n-nodes-base.code",
"typeVersion": 2, "typeVersion": 2,
@@ -2127,7 +2208,7 @@
}, },
{ {
"parameters": { "parameters": {
"jsCode": "const original = $('IF - Tiene file_id?').item.json || {};\nconst errorData = $json || {};\n\nfunction stringifyError(value) {\n try {\n if (typeof value === 'string') return value;\n return JSON.stringify(value).slice(0, 500);\n } catch (error) {\n return String(value).slice(0, 500);\n }\n}\n\nreturn [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time || '',\n ultimo_refresco_ia: new Date().toISOString(),\n refresh_status: 'ERROR_METADATA_DRIVE',\n refresh_attempts: Number(original.refresh_attempts_actual || original.refresh_attempts || 0) + 1,\n procesado_ia: false,\n motivos_revision: `error_metadata_drive: ${stringifyError(errorData)}`,\n\n converted_google_slides_id: original.converted_google_slides_id || '',\n converted_google_slides_link: original.converted_google_slides_link || '',\n },\n },\n];" "jsCode": "const original = $('IF - Tiene file_id?').item.json || {};\nconst errorData = $json || {};\n\nfunction stringifyError(value) {\n try {\n if (typeof value === 'string') return value;\n return JSON.stringify(value).slice(0, 500);\n } catch (error) {\n return String(value).slice(0, 500);\n }\n}\n\nreturn [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time || '',\n ultimo_refresco_ia: original.ultimo_refresco_ia || '',\n ultima_actualizacion: new Date().toISOString(),\n refresh_status: 'ERROR_METADATA_DRIVE',\n refresh_attempts: Number(original.refresh_attempts_actual || original.refresh_attempts || 0) + 1,\n procesado_ia: false,\n motivos_revision: `error_metadata_drive: ${stringifyError(errorData)}`,\n\n converted_google_slides_id: original.converted_google_slides_id || '',\n converted_google_slides_link: original.converted_google_slides_link || '',\n },\n },\n];"
}, },
"type": "n8n-nodes-base.code", "type": "n8n-nodes-base.code",
"typeVersion": 2, "typeVersion": 2,
@@ -2140,7 +2221,7 @@
}, },
{ {
"parameters": { "parameters": {
"jsCode": "function getNodeJson(name) {\n try {\n return $(name).item.json || {};\n } catch (error) {\n return {};\n }\n}\n\nconst evaluated = getNodeJson('Code - Evaluar refresco');\nconst converted = getNodeJson('Code - Preparar ID convertido');\n\nconst original = Object.keys(converted).length ? converted : evaluated;\nconst errorData = $json || {};\n\nfunction stringifyError(value) {\n try {\n if (typeof value === 'string') return value;\n return JSON.stringify(value).slice(0, 500);\n } catch (error) {\n return String(value).slice(0, 500);\n }\n}\n\nreturn [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time_actual || original.drive_modified_time || '',\n ultimo_refresco_ia: new Date().toISOString(),\n refresh_status: 'ERROR_EXPORT_SLIDES',\n refresh_attempts: Number(original.refresh_attempts_actual || original.refresh_attempts || 0) + 1,\n procesado_ia: false,\n motivos_revision: `error_export_slides: ${stringifyError(errorData)}`,\n\n converted_google_slides_id: original.converted_google_slides_id || '',\n converted_google_slides_link: original.converted_google_slides_link || '',\n },\n },\n];" "jsCode": "function getNodeJson(name) {\n try {\n return $(name).item.json || {};\n } catch (error) {\n return {};\n }\n}\n\nconst evaluated = getNodeJson('Code - Evaluar refresco');\nconst converted = getNodeJson('Code - Preparar ID convertido');\n\nconst original = Object.keys(converted).length ? converted : evaluated;\nconst errorData = $json || {};\n\nfunction stringifyError(value) {\n try {\n if (typeof value === 'string') return value;\n return JSON.stringify(value).slice(0, 500);\n } catch (error) {\n return String(value).slice(0, 500);\n }\n}\n\nreturn [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time_actual || original.drive_modified_time || '',\n ultimo_refresco_ia: original.ultimo_refresco_ia || '',\n ultima_actualizacion: new Date().toISOString(),\n refresh_status: 'ERROR_EXPORT_SLIDES',\n refresh_attempts: Number(original.refresh_attempts_actual || original.refresh_attempts || 0) + 1,\n procesado_ia: false,\n motivos_revision: `error_export_slides: ${stringifyError(errorData)}`,\n\n converted_google_slides_id: original.converted_google_slides_id || '',\n converted_google_slides_link: original.converted_google_slides_link || '',\n },\n },\n];"
}, },
"type": "n8n-nodes-base.code", "type": "n8n-nodes-base.code",
"typeVersion": 2, "typeVersion": 2,
@@ -2227,7 +2308,7 @@
}, },
{ {
"parameters": { "parameters": {
"jsCode": "const original = $('IF - Es Google Slides nativo?').item.json || {};\nconst response = $json || {};\n\nfunction clean(value) {\n return String(value ?? '').trim();\n}\n\nif (!response.ok || !response.converted_google_slides_id) {\n return [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time_actual || original.drive_modified_time || '',\n ultimo_refresco_ia: new Date().toISOString(),\n refresh_status: 'ERROR_CONVERT_PPTX',\n refresh_attempts: Number(original.refresh_attempts_actual || original.refresh_attempts || 0) + 1,\n procesado_ia: false,\n motivos_revision: `error_convert_pptx: ${clean(response.error || JSON.stringify(response)).slice(0, 300)}`,\n },\n },\n ];\n}\n\nreturn [\n {\n json: {\n ...original,\n\n drive_file_id_original: original.drive_file_id || original.file_id_refresco || '',\n drive_file_id: response.converted_google_slides_id,\n drive_mime_type: 'application/vnd.google-apps.presentation',\n\n converted_google_slides_id: response.converted_google_slides_id,\n converted_google_slides_link: response.converted_google_slides_link,\n\n refresh_status: 'CONVERTIDO_PPTX',\n },\n },\n];" "jsCode": "const original = $('IF - Es Google Slides nativo?').item.json || {};\nconst response = $json || {};\n\nfunction clean(value) {\n return String(value ?? '').trim();\n}\n\nif (!response.ok || !response.converted_google_slides_id) {\n return [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time_actual || original.drive_modified_time || '',\n ultimo_refresco_ia: original.ultimo_refresco_ia || '',\n ultima_actualizacion: new Date().toISOString(),\n refresh_status: 'ERROR_CONVERT_PPTX',\n refresh_attempts: Number(original.refresh_attempts_actual || original.refresh_attempts || 0) + 1,\n procesado_ia: false,\n motivos_revision: `error_convert_pptx: ${clean(response.error || JSON.stringify(response)).slice(0, 300)}`,\n },\n },\n ];\n}\n\nreturn [\n {\n json: {\n ...original,\n\n drive_file_id_original: original.drive_file_id || original.file_id_refresco || '',\n drive_file_id: response.converted_google_slides_id,\n drive_mime_type: 'application/vnd.google-apps.presentation',\n\n converted_google_slides_id: response.converted_google_slides_id,\n converted_google_slides_link: response.converted_google_slides_link,\n\n refresh_status: 'CONVERTIDO_PPTX',\n },\n },\n];"
}, },
"type": "n8n-nodes-base.code", "type": "n8n-nodes-base.code",
"typeVersion": 2, "typeVersion": 2,
@@ -2306,7 +2387,7 @@
}, },
{ {
"parameters": { "parameters": {
"jsCode": "const original = $json || {};\n\nreturn [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time_actual || original.drive_modified_time || '',\n ultimo_refresco_ia: new Date().toISOString(),\n refresh_status: 'PPTX_DEMASIADO_PESADO',\n refresh_attempts: Number(original.refresh_attempts_actual || original.refresh_attempts || 0) + 1,\n procesado_ia: false,\n motivos_revision: `pptx_demasiado_pesado_para_conversion_google_slides: ${original.drive_size_mb || 0} MB. Google Slides solo permite convertir presentaciones hasta 100 MB.`,\n\n converted_google_slides_id: original.converted_google_slides_id || '',\n converted_google_slides_link: original.converted_google_slides_link || '',\n },\n },\n];" "jsCode": "const original = $json || {};\n\nreturn [\n {\n json: {\n ...original,\n drive_modified_time: original.drive_modified_time_actual || original.drive_modified_time || '',\n ultimo_refresco_ia: original.ultimo_refresco_ia || '',\n ultima_actualizacion: new Date().toISOString(),\n refresh_status: 'PPTX_DEMASIADO_PESADO',\n refresh_attempts: Number(original.refresh_attempts_actual || original.refresh_attempts || 0) + 1,\n procesado_ia: false,\n motivos_revision: `pptx_demasiado_pesado_para_conversion_google_slides: ${original.drive_size_mb || 0} MB. Google Slides solo permite convertir presentaciones hasta 100 MB.`,\n\n converted_google_slides_id: original.converted_google_slides_id || '',\n converted_google_slides_link: original.converted_google_slides_link || '',\n },\n },\n];"
}, },
"type": "n8n-nodes-base.code", "type": "n8n-nodes-base.code",
"typeVersion": 2, "typeVersion": 2,
@@ -2380,6 +2461,54 @@
], ],
"id": "fc72547c-317e-4ac7-9428-825e8504f143", "id": "fc72547c-317e-4ac7-9428-825e8504f143",
"name": "Sticky Note3" "name": "Sticky Note3"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "cb44c645-00ba-43c1-a8f0-2fe556ebc382",
"leftValue": "={{ String($json.tablero_sync_key || '').trim() }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
5024,
112
],
"id": "1b56b546-c4af-412d-bef8-8105eb830ae1",
"name": "IF - Tiene sync key antes de Upsert?"
},
{
"parameters": {
"amount": 4
},
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [
2768,
432
],
"id": "be090d5c-6fb6-4087-b25b-83fb8e3a3392",
"name": "Wait - Respetar cuota Google Sheets",
"webhookId": "8b997d7c-097c-4c8a-8793-6bce3db5e142"
} }
], ],
"connections": { "connections": {
@@ -2566,7 +2695,7 @@
"main": [ "main": [
[ [
{ {
"node": "Google Sheets - Upsert Banco Fulgencio", "node": "IF - Tiene sync key antes de Upsert?",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -2577,7 +2706,7 @@
"main": [ "main": [
[ [
{ {
"node": "Google Sheets - Upsert Banco Fulgencio", "node": "IF - Tiene sync key antes de Upsert?",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -2588,7 +2717,7 @@
"main": [ "main": [
[ [
{ {
"node": "Loop - Filas Banco Fulgencio", "node": "Wait - Respetar cuota Google Sheets",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -2599,7 +2728,7 @@
"main": [ "main": [
[ [
{ {
"node": "Google Sheets - Upsert Banco Fulgencio", "node": "IF - Tiene sync key antes de Upsert?",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -2610,7 +2739,7 @@
"main": [ "main": [
[ [
{ {
"node": "Google Sheets - Upsert Banco Fulgencio", "node": "IF - Tiene sync key antes de Upsert?",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -2621,7 +2750,7 @@
"main": [ "main": [
[ [
{ {
"node": "Google Sheets - Upsert Banco Fulgencio", "node": "IF - Tiene sync key antes de Upsert?",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -2679,7 +2808,7 @@
], ],
[ [
{ {
"node": "Google Sheets - Upsert Banco Fulgencio", "node": "IF - Tiene sync key antes de Upsert?",
"type": "main", "type": "main",
"index": 0 "index": 0
} }
@@ -2705,6 +2834,17 @@
] ]
}, },
"Code - Marcar PPTX demasiado pesado": { "Code - Marcar PPTX demasiado pesado": {
"main": [
[
{
"node": "IF - Tiene sync key antes de Upsert?",
"type": "main",
"index": 0
}
]
]
},
"IF - Tiene sync key antes de Upsert?": {
"main": [ "main": [
[ [
{ {
@@ -2712,38 +2852,40 @@
"type": "main", "type": "main",
"index": 0 "index": 0
} }
],
[
{
"node": "Loop - Filas Banco Fulgencio",
"type": "main",
"index": 0
}
]
]
},
"Wait - Respetar cuota Google Sheets": {
"main": [
[
{
"node": "Loop - Filas Banco Fulgencio",
"type": "main",
"index": 0
}
] ]
] ]
} }
}, },
"authors": "Isaac Aracena", "authors": "Isaac Aracena",
"name": "Version a92fd2c1", "name": "Version db8caedf",
"description": "", "description": "",
"autosaved": true, "autosaved": true,
"workflowPublishHistory": [ "workflowPublishHistory": [
{ {
"createdAt": "2026-07-22T14:43:58.310Z", "createdAt": "2026-07-27T14:23:46.606Z",
"id": 3376, "id": 3494,
"workflowId": "g2JtKXNz5GDbkmQH", "workflowId": "g2JtKXNz5GDbkmQH",
"versionId": "a92fd2c1-b300-4def-8cd7-fdb0bde1bdfe", "versionId": "db8caedf-99e0-4c41-b43a-4b115621fb4a",
"event": "activated", "event": "activated",
"userId": "0a88c0b1-928e-4412-896e-c5d1c99b2029" "userId": "0a88c0b1-928e-4412-896e-c5d1c99b2029"
},
{
"createdAt": "2026-07-25T17:10:33.819Z",
"id": 3428,
"workflowId": "g2JtKXNz5GDbkmQH",
"versionId": "a92fd2c1-b300-4def-8cd7-fdb0bde1bdfe",
"event": "activated",
"userId": "0a88c0b1-928e-4412-896e-c5d1c99b2029"
},
{
"createdAt": "2026-07-25T17:10:33.748Z",
"id": 3427,
"workflowId": "g2JtKXNz5GDbkmQH",
"versionId": "a92fd2c1-b300-4def-8cd7-fdb0bde1bdfe",
"event": "deactivated",
"userId": "0a88c0b1-928e-4412-896e-c5d1c99b2029"
} }
] ]
} }