AutomationFlowsData & Sheets › Parser Venta

Parser Venta

parser_venta. Uses postgres, httpRequest. Webhook trigger; 11 nodes.

Webhook trigger★★★★☆ complexity11 nodesPostgresHTTP Request
Data & Sheets Trigger: Webhook Nodes: 11 Complexity: ★★★★☆ Added:

This workflow follows the HTTP Request → Postgres recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "parser_venta",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "wa/venta",
        "responseMode": "responseNode"
      },
      "id": "wh-venta",
      "name": "WhatsApp In",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Parsea el body de WhatsApp Cloud API y devuelve { texto, telefono }\nconst body = items[0].json.body || items[0].json;\nconst entry = body.entry?.[0]?.changes?.[0]?.value;\nconst msg   = entry?.messages?.[0];\nif (!msg || msg.type !== 'text') return [];\nreturn [{ json: { texto: msg.text.body, telefono: msg.from } }];"
      },
      "id": "extract",
      "name": "Extraer texto",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Parser del formato #VENTA. Devuelve estructura limpia.\nconst t = items[0].json.texto;\nconst tel = items[0].json.telefono;\nif (!/^#VENTA/i.test(t.trim())) return [];\n\nfunction grab(label) {\n  const m = t.match(new RegExp('^\\\\s*' + label + '\\\\s*:\\\\s*(.+)$', 'im'));\n  return m ? m[1].trim() : null;\n}\nfunction grabBlock(label) {\n  const re = new RegExp(label + ':\\\\s*\\\\n([\\\\s\\\\S]*?)(?=\\\\n[A-Z_]+:|$)', 'i');\n  const m = t.match(re); return m ? m[1] : '';\n}\n\nconst fecha   = grab('FECHA') || new Date().toISOString().slice(0,10);\nconst total   = parseFloat((grab('TOTAL')||'0').replace(/[^0-9.]/g,''));\nconst reserva = parseFloat((grab('RESERVA')||'0').replace(/[^0-9.]/g,''));\nconst envio   = parseFloat((grab('ENVIO')||'0').replace(/[^0-9.]/g,''));\nconst notas   = grab('NOTAS') || '';\n\nconst clienteBlock = grabBlock('CLIENTE');\nconst c = {};\nfor (const line of clienteBlock.split(/\\n/)) {\n  const m = line.match(/^\\s*(\\w+)\\s*:\\s*(.+)$/);\n  if (m) c[m[1].toLowerCase()] = m[2].trim();\n}\n\nconst prodBlock = grabBlock('PRODUCTOS');\nconst lineas = [];\nfor (const raw of prodBlock.split(/\\n/)) {\n  const line = raw.replace(/^\\s*[-+]\\s*/, '').trim();\n  if (!line) continue;\n  const parts = line.split('|').map(s=>s.trim());\n  const producto = parts.shift();\n  const attrs = {};\n  let cant = 1, precio = null;\n  for (const p of parts) {\n    const [k,v] = p.split('=').map(s=>s.trim());\n    if (!k) continue;\n    if (k.toLowerCase()==='cant')   cant = parseInt(v,10) || 1;\n    else if (k.toLowerCase()==='precio') precio = parseFloat(v);\n    else attrs[k.toLowerCase()] = v;\n  }\n  lineas.push({ producto, attrs, cant, precio });\n}\n\nreturn [{ json: { vendedor_tel: tel, fecha, total, reserva, envio, notas, cliente: c, lineas } }];"
      },
      "id": "parse",
      "name": "Parser #VENTA",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        640,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT proximo_numero('OV') AS numero;"
      },
      "id": "num",
      "name": "Pr\u00f3ximo N\u00ba OV",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        840,
        300
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "WITH up_cli AS (\n  INSERT INTO clientes (nombre, dni, email, telefono, direccion, entre_calles, cp)\n  VALUES ($1, $2, $3, $4, $5, $6, $7)\n  RETURNING id\n),\nup_ven AS (\n  INSERT INTO vendedores (nombre, whatsapp) VALUES ('auto', $8)\n  ON CONFLICT (whatsapp) DO UPDATE SET activo = TRUE\n  RETURNING id\n),\nins_ov AS (\n  INSERT INTO ordenes_venta (numero, fecha, cliente_id, vendedor_id, estado, total, reserva, adeudado, costo_envio, notas)\n  VALUES ($9, $10::date, (SELECT id FROM up_cli), (SELECT id FROM up_ven),\n          'borrador', $11, $12, $11 - $12, $13, $14)\n  RETURNING id, numero\n)\nSELECT * FROM ins_ov;",
        "options": {
          "queryReplacement": "={{ $json.cliente.nombre }},{{ $json.cliente.dni }},{{ $json.cliente.email }},{{ $json.cliente.tel }},{{ $json.cliente.direccion }},{{ $json.cliente.entre_calles }},{{ $json.cliente.cp }},{{ $json.vendedor_tel }},{{ $node['Pr\u00f3ximo N\u00ba OV'].json.numero }},{{ $json.fecha }},{{ $json.total }},{{ $json.reserva }},{{ $json.envio }},{{ $json.notas }}"
        }
      },
      "id": "ins-ov",
      "name": "Insert OV + cliente",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        1060,
        300
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Para cada l\u00ednea pedida resuelve la variante o falla con error legible.\nconst ov = items[0].json;\nconst src = $node['Parser #VENTA'].json;\nconst out = [];\nfor (const l of src.lineas) {\n  out.push({ json: { ov_id: ov.id, ov_numero: ov.numero, linea: l } });\n}\nreturn out;"
      },
      "id": "expand",
      "name": "Expandir l\u00edneas",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1280,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "-- Busca variante exacta por nombre de producto + set de valores de atributos (clave=valor).\nWITH attrs AS (\n  SELECT key AS atributo, value AS valor\n  FROM jsonb_each_text($1::jsonb)\n),\np AS (SELECT id FROM productos WHERE lower(nombre) = lower($2))\nSELECT v.id, v.sku, COALESCE(v.precio, pr.precio_base) AS precio\nFROM variantes v JOIN productos pr ON pr.id = v.producto_id\nWHERE v.producto_id = (SELECT id FROM p)\n  AND NOT EXISTS (\n    SELECT 1 FROM attrs a\n    WHERE NOT EXISTS (\n      SELECT 1 FROM variante_valores vv\n      JOIN valores_atributo va ON va.id = vv.valor_atributo_id\n      JOIN atributos at ON at.id = va.atributo_id\n      WHERE vv.variante_id = v.id\n        AND lower(at.nombre) = lower(a.atributo)\n        AND lower(va.valor)  = lower(a.valor)\n    )\n  )\nLIMIT 1;",
        "options": {
          "queryReplacement": "={{ JSON.stringify($json.linea.attrs) }},{{ $json.linea.producto }}"
        }
      },
      "id": "find-var",
      "name": "Resolver variante",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        1500,
        300
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO lineas_ov (ov_id, variante_id, cantidad, precio_unit)\nVALUES ($1, $2, $3, $4) RETURNING id;",
        "options": {
          "queryReplacement": "={{ $node['Expandir l\u00edneas'].json.ov_id }},{{ $json.id }},{{ $node['Expandir l\u00edneas'].json.linea.cant }},{{ $node['Expandir l\u00edneas'].json.linea.precio || $json.precio }}"
        }
      },
      "id": "ins-lin",
      "name": "Insert l\u00ednea OV",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        1720,
        300
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "url": "=http://n8n:5678/webhook/internal/stock_y_oc",
        "method": "POST",
        "jsonBody": "={ \"ov_id\": {{ $node['Insert OV + cliente'].json.id }} }",
        "sendBody": true
      },
      "id": "trigger-stock",
      "name": "Disparar stock_y_oc",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1940,
        300
      ]
    },
    {
      "parameters": {
        "url": "=https://graph.facebook.com/v20.0/{{$env.WHATSAPP_PHONE_ID}}/messages",
        "method": "POST",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{$env.WHATSAPP_TOKEN}}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "jsonBody": "={\n  \"messaging_product\": \"whatsapp\",\n  \"to\": \"{{ $node['Parser #VENTA'].json.vendedor_tel }}\",\n  \"type\": \"text\",\n  \"text\": { \"body\": \"OV creada: {{ $node['Insert OV + cliente'].json.numero }}. Te aviso cuando est\u00e9 el link de pago y la OC al proveedor.\" }\n}"
      },
      "id": "wa-out",
      "name": "Confirmar al vendedor",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2160,
        300
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={ \"ok\": true, \"ov\": $node['Insert OV + cliente'].json.numero }"
      },
      "id": "resp",
      "name": "Responder OK",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        2380,
        300
      ]
    }
  ],
  "connections": {
    "WhatsApp In": {
      "main": [
        [
          {
            "node": "Extraer texto",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extraer texto": {
      "main": [
        [
          {
            "node": "Parser #VENTA",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parser #VENTA": {
      "main": [
        [
          {
            "node": "Pr\u00f3ximo N\u00ba OV",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pr\u00f3ximo N\u00ba OV": {
      "main": [
        [
          {
            "node": "Insert OV + cliente",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert OV + cliente": {
      "main": [
        [
          {
            "node": "Expandir l\u00edneas",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Expandir l\u00edneas": {
      "main": [
        [
          {
            "node": "Resolver variante",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Resolver variante": {
      "main": [
        [
          {
            "node": "Insert l\u00ednea OV",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert l\u00ednea OV": {
      "main": [
        [
          {
            "node": "Disparar stock_y_oc",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Disparar stock_y_oc": {
      "main": [
        [
          {
            "node": "Confirmar al vendedor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Confirmar al vendedor": {
      "main": [
        [
          {
            "node": "Responder OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

parser_venta. Uses postgres, httpRequest. Webhook trigger; 11 nodes.

Source: https://github.com/bytemok/P/blob/420be2ab06f74f6499aa4a2b44761aec596e2005/workflows/parser_venta.json — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Data & Sheets

Scraping. Uses httpRequest, postgres, @apify/n8n-nodes-apify, respondToWebhook. Webhook trigger; 61 nodes.

HTTP Request, Postgres, @Apify/N8N Nodes Apify
Data & Sheets

Workflow B — AI Listing Engine. Uses httpRequest, postgres, errorTrigger. Webhook trigger; 47 nodes.

HTTP Request, Postgres, Error Trigger
Data & Sheets

Fluxo de voluntárias ZendeskXANXBD. Uses functionItem, zendesk, httpRequest, postgres. Webhook trigger; 25 nodes.

Function Item, Zendesk, HTTP Request +1
Data & Sheets

Fluxo de voluntárias ZendeskXANXBD. Uses functionItem, zendesk, httpRequest, postgres. Webhook trigger; 25 nodes.

Function Item, Zendesk, HTTP Request +1
Data & Sheets

Fluxo de voluntárias ZendeskXANXBD. Uses functionItem, zendesk, httpRequest, postgres. Webhook trigger; 25 nodes.

Function Item, Zendesk, HTTP Request +1