{
 "name": "La Llave · stock bajo → pedido al proveedor",
 "nodes": [
  {
   "id": "691ae080-d2cb-4d2b-b7b3-d8cdbc17f6cf",
   "name": "Nota bc11",
   "type": "n8n-nodes-base.stickyNote",
   "typeVersion": 1,
   "position": [
    -40,
    -320
   ],
   "parameters": {
    "content": "## Stock bajo → pedido al proveedor\nCada venta del mostrador descuenta el stock. Lo que queda por debajo del mínimo se junta por proveedor y sale un pedido por mail a cada uno (reponiendo hasta el nivel de cada producto). El dueño recibe el aviso por Telegram. Sin IA: son cuentas.",
    "width": 560,
    "height": 190,
    "color": 5
   }
  },
  {
   "id": "29333d49-d662-4ab8-ac2e-7cc163b8e90a",
   "name": "Venta del mostrador",
   "type": "n8n-nodes-base.webhook",
   "typeVersion": 2.1,
   "position": [
    0,
    0
   ],
   "parameters": {
    "httpMethod": "POST",
    "path": "venta-la-llave",
    "responseMode": "onReceived",
    "options": {}
   },
   "webhookId": "5d61fc39-d515-476b-9d8c-795708d8d3d8"
  },
  {
   "id": "3d2c26f0-bc6b-437a-bc60-46a86c5eb1de",
   "name": "Stock actual",
   "type": "n8n-nodes-base.dataTable",
   "typeVersion": 1.1,
   "position": [
    240,
    0
   ],
   "parameters": {
    "resource": "row",
    "operation": "get",
    "dataTableId": {
     "__rl": true,
     "mode": "id",
     "value": ""
    },
    "matchType": "allConditions",
    "returnAll": true
   },
   "executeOnce": true
  },
  {
   "id": "46e0ab94-047d-4de5-9332-b09c8fceb9f3",
   "name": "Descontar la venta",
   "type": "n8n-nodes-base.code",
   "typeVersion": 2,
   "position": [
    480,
    0
   ],
   "parameters": {
    "mode": "runOnceForAllItems",
    "jsCode": "\n// Descuenta lo vendido del stock actual y marca lo que quedó por debajo del mínimo. Una salida por producto vendido.\nconst venta = $('Venta del mostrador').first().json.body || {};\nconst stock = Object.fromEntries($input.all().map(i => [i.json.sku, i.json]));\nconst salida = [];\nfor (const it of venta.items || []) {\n  const p = stock[it.sku]; if (!p) continue;\n  const quedan = Math.max(0, Number(p.stock) - Math.max(0, Number(it.cantidad) || 0));\n  salida.push({ json: { id: p.id, sku: p.sku, producto: p.producto, vendidos: Number(it.cantidad), antes: Number(p.stock), stock: quedan,\n    minimo: Number(p.minimo), reponer_hasta: Number(p.reponer_hasta), proveedor: p.proveedor, email_proveedor: p.email_proveedor,\n    costo: Number(p.costo), bajo_minimo: quedan < Number(p.minimo), ticket: venta.ticket } });\n}\nreturn salida;\n"
   }
  },
  {
   "id": "6587f373-9be4-4dfe-8d24-89df54ae2aa4",
   "name": "Actualizar el stock",
   "type": "n8n-nodes-base.dataTable",
   "typeVersion": 1.1,
   "position": [
    720,
    0
   ],
   "parameters": {
    "resource": "row",
    "operation": "update",
    "dataTableId": {
     "__rl": true,
     "mode": "id",
     "value": ""
    },
    "columns": {
     "mappingMode": "defineBelow",
     "value": {
      "stock": "={{ $json.stock }}"
     },
     "matchingColumns": [],
     "schema": [
      {
       "id": "stock",
       "displayName": "stock",
       "required": false,
       "defaultMatch": false,
       "display": true,
       "type": "number",
       "canBeUsedToMatch": true
      }
     ]
    },
    "options": {},
    "filters": {
     "conditions": [
      {
       "keyName": "sku",
       "condition": "eq",
       "keyValue": "={{ $json.sku }}"
      }
     ]
    }
   }
  },
  {
   "id": "c0de3d6c-6ab1-4fd8-ab8c-51b2e77a557d",
   "name": "¿Quedó bajo el mínimo?",
   "type": "n8n-nodes-base.if",
   "typeVersion": 2.3,
   "position": [
    960,
    0
   ],
   "parameters": {
    "conditions": {
     "options": {
      "caseSensitive": true,
      "leftValue": "",
      "typeValidation": "loose",
      "version": 2
     },
     "conditions": [
      {
       "id": "b49199c5-9754-4370-bb73-333b78555472",
       "leftValue": "={{ $('Descontar la venta').item.json.bajo_minimo }}",
       "rightValue": "",
       "operator": {
        "type": "boolean",
        "operation": "true"
       }
      }
     ],
     "combinator": "and"
    },
    "options": {}
   }
  },
  {
   "id": "34088aa6-12b1-4684-82dd-de8af723f9ca",
   "name": "Armar el pedido por proveedor",
   "type": "n8n-nodes-base.code",
   "typeVersion": 2,
   "position": [
    1200,
    -80
   ],
   "parameters": {
    "mode": "runOnceForAllItems",
    "jsCode": "\n// Un pedido por proveedor con todo lo que quedó bajo el mínimo: se repone hasta el nivel «reponer_hasta».\nconst $$ = n => '$ ' + Math.round(n).toLocaleString('es-AR');\nconst vendidos = $('Descontar la venta').all().map(i => i.json);   // el IF deja pasar las filas de la tabla; los datos de la venta están acá\nconst por = {};\nfor (const p of $input.all().map(i => vendidos.find(v => v.sku === i.json.sku)).filter(Boolean)) {\n  const pedir = Math.max(0, p.reponer_hasta - p.stock);\n  (por[p.proveedor] ||= { proveedor: p.proveedor, email: p.email_proveedor, items: [] }).items.push({ ...p, pedir, subtotal: pedir * p.costo });\n}\nreturn Object.values(por).map(o => {\n  const total = o.items.reduce((s, i) => s + i.subtotal, 0);\n  const filas = o.items.map(i => `<tr><td>${i.sku}</td><td>${i.producto}</td><td style=\"text-align:right\">${i.pedir}</td></tr>`).join('');\n  return { json: { ...o, total, total_texto: $$(total), cantidad: o.items.length,\n    resumen: o.items.map(i => `${i.producto}: quedan ${i.stock} (mínimo ${i.minimo}) → pedir ${i.pedir}`).join('\\n'),\n    html: `<p>Hola, equipo de ${o.proveedor}:</p><p>Les pedimos para reponer:</p><table cellpadding=\"6\" style=\"border-collapse:collapse;font-family:Arial,sans-serif;font-size:14px\"><tr><th align=\"left\">Código</th><th align=\"left\">Producto</th><th align=\"right\">Cantidad</th></tr>${filas}</table><p>Por favor confirmen precio y fecha de entrega.</p><p>Ferretería La Llave</p><p style=\"font-size:11px;color:#777\">Demostración: La Llave es un negocio ficticio y este correo quedó en un buzón de prueba.</p>` } };\n});\n"
   }
  },
  {
   "id": "a35defa7-72bd-48c3-aa92-9a2e643e76d3",
   "name": "Pedido al proveedor",
   "type": "n8n-nodes-base.emailSend",
   "typeVersion": 2.1,
   "position": [
    1440,
    -160
   ],
   "parameters": {
    "fromEmail": "Ferretería La Llave <compras@lallave.demo>",
    "toEmail": "={{ $json.email }}",
    "subject": "={{ 'Pedido de reposición · Ferretería La Llave · ' + $json.cantidad + ($json.cantidad === 1 ? ' producto' : ' productos') }}",
    "emailFormat": "html",
    "html": "={{ $json.html }}",
    "options": {
     "appendAttribution": false
    }
   },
   "credentials": {
    "smtp": {
     "id": "",
     "name": "Buzón de prueba (Mailpit)"
    }
   }
  },
  {
   "id": "9f2bc8a0-20e1-47d1-add1-20aac9a45f94",
   "name": "Aviso al dueño por Telegram",
   "type": "n8n-nodes-base.telegram",
   "typeVersion": 1.2,
   "position": [
    1440,
    40
   ],
   "parameters": {
    "resource": "message",
    "operation": "sendMessage",
    "chatId": "5550100",
    "text": "={{ '<b>Pedido enviado a ' + $('Armar el pedido por proveedor').item.json.proveedor + '</b> (' + $('Armar el pedido por proveedor').item.json.total_texto + ')\\n' + $('Armar el pedido por proveedor').item.json.resumen }}",
    "additionalFields": {
     "parse_mode": "HTML",
     "appendAttribution": false
    }
   },
   "credentials": {
    "telegramApi": {
     "id": "",
     "name": "Telegram (simulador)"
    }
   }
  }
 ],
 "connections": {
  "Venta del mostrador": {
   "main": [
    [
     {
      "node": "Stock actual",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Stock actual": {
   "main": [
    [
     {
      "node": "Descontar la venta",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Descontar la venta": {
   "main": [
    [
     {
      "node": "Actualizar el stock",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Actualizar el stock": {
   "main": [
    [
     {
      "node": "¿Quedó bajo el mínimo?",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "¿Quedó bajo el mínimo?": {
   "main": [
    [
     {
      "node": "Armar el pedido por proveedor",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Armar el pedido por proveedor": {
   "main": [
    [
     {
      "node": "Pedido al proveedor",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Pedido al proveedor": {
   "main": [
    [
     {
      "node": "Aviso al dueño por Telegram",
      "type": "main",
      "index": 0
     }
    ]
   ]
  }
 },
 "settings": {
  "executionOrder": "v1",
  "timezone": "America/Argentina/Buenos_Aires",
  "saveManualExecutions": true
 }
}