From 7cefa3d7e004611df151af67cd956aea58ba69c6 Mon Sep 17 00:00:00 2001 From: adminjeroen Date: Fri, 3 Jul 2026 07:39:17 +0200 Subject: [PATCH] Upload files to "palletways" --- .../Palletways leveringen - outbound.json | 643 ++++++++++++++++++ palletways/Pre-Alert Kwetsbare Zending.json | 182 +++++ palletways/pw prijzen.json | 348 ++++++++++ 3 files changed, 1173 insertions(+) create mode 100644 palletways/Palletways leveringen - outbound.json create mode 100644 palletways/Pre-Alert Kwetsbare Zending.json create mode 100644 palletways/pw prijzen.json diff --git a/palletways/Palletways leveringen - outbound.json b/palletways/Palletways leveringen - outbound.json new file mode 100644 index 0000000..ba92457 --- /dev/null +++ b/palletways/Palletways leveringen - outbound.json @@ -0,0 +1,643 @@ +{ + "name": "Palletways leveringen - outbound", + "nodes": [ + { + "parameters": { + "jsCode": "// Get today's date\nconst today = new Date();\n\n\nconst lastWorkday = new Date(today);\n\n// Format as YYYY-MM-DD\nconst yyyy = lastWorkday.getFullYear();\nconst mm = String(lastWorkday.getMonth() + 1).padStart(2, '0');\nconst dd = String(lastWorkday.getDate()).padStart(2, '0');\nconst formattedDate = `${yyyy}-${mm}-${dd}`;\n\nreturn [\n {\n json: {\n lastWorkday: formattedDate\n }\n }\n];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 0, + 176 + ], + "id": "79604baf-6a53-4f92-8d60-6a9517c89753", + "name": "Last workday1" + }, + { + "parameters": { + "jsCode": "/**\n * n8n “Function” node\n * Input : first item’s json.data (XML string)\n * Output: one item per unique , shaped as { json: { dc_syscon: \"\" } }\n */\n\n// 1️⃣ Grab the XML from the first incoming item\nconst xmlData = items[0]?.json?.data;\nif (typeof xmlData !== 'string') {\n\tthrow new Error('Expected an XML string in $input.first().json.data');\n}\n\n// 2️⃣ Collect every ... match\nconst dcSysconValues = [];\nconst regex = /(.*?)<\\/TrackingID>/g;\nlet match;\nwhile ((match = regex.exec(xmlData)) !== null) {\n\tdcSysconValues.push(match[1].trim().toUpperCase());\n}\n\n// 3️⃣ De‑duplicate\nconst uniqueValues = [...new Set(dcSysconValues)];\n\n// 4️⃣ Build one output item per unique value\nreturn uniqueValues.map(value => ({\n\tjson: { trackingid: value },\n}));\n" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 448, + 176 + ], + "id": "86570f68-7269-41fe-b468-af52f4d16b02", + "name": "Code" + }, + { + "parameters": { + "url": "=https://api.palletways.com/lookupDepotNo/{{ $json.Response.Detail.Data.Manifest.Depot.Number }}?apikey=SXJaM7PLjZDqfsnXSDP8Y9wDY6crxJySBg705MQEPus%3D", + "options": { + "response": { + "response": { + "responseFormat": "text" + } + }, + "timeout": 10000 + } + }, + "id": "814a05a5-b8c8-4a26-bfed-7a5744e33851", + "name": "Get Paying Depot Address", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.1, + "position": [ + 1360, + 48 + ], + "continueOnFail": true + }, + { + "parameters": { + "options": { + "explicitArray": false, + "mergeAttrs": true + } + }, + "id": "e666bcfa-ac35-46f2-8179-4610923cf64f", + "name": "Parse Paying Depot XML", + "type": "n8n-nodes-base.xml", + "typeVersion": 1, + "position": [ + 1584, + 48 + ] + }, + { + "parameters": { + "options": { + "explicitArray": false, + "mergeAttrs": true + } + }, + "id": "2d662be5-4701-4f75-8d4e-1aa06c3813ad", + "name": "Parse XML Response", + "type": "n8n-nodes-base.xml", + "typeVersion": 1, + "position": [ + 1200, + 176 + ] + }, + { + "parameters": { + "mode": "combine", + "combineBy": "combineByPosition", + "numberInputs": 3, + "options": {} + }, + "type": "n8n-nodes-base.merge", + "typeVersion": 3.2, + "position": [ + 1904, + 160 + ], + "id": "fb7a9070-3276-4800-98e6-7b625c2a9875", + "name": "Merge" + }, + { + "parameters": { + "url": "=https://api.palletways.com/getconsignment/{{ $json.trackingid }}?apikey=SXJaM7PLjZDqfsnXSDP8Y9wDY6crxJySBg705MQEPus%3D", + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + 1008, + 176 + ], + "id": "5a484f33-249d-46af-b664-cdecbb16a2ee", + "name": "Get Consigment" + }, + { + "parameters": { + "fileFormat": "csv", + "options": { + "headerRow": false, + "includeEmptyCells": true + } + }, + "id": "b7226c3d-4d33-4547-9617-020e5c62c164", + "name": "Import CSV", + "type": "n8n-nodes-base.spreadsheetFile", + "position": [ + 256, + 0 + ], + "typeVersion": 2 + }, + { + "parameters": { + "url": "https://portal.palletways.com/api/Planning/?apikey=SXJaM7PLjZDqfsnXSDP8Y9wDY6crxJySBg705MQEPus%3D&output=csv", + "options": { + "response": { + "response": { + "responseFormat": "file" + } + } + } + }, + "id": "874b6714-43dc-44d2-912f-a212c410e084", + "name": "Download CSV", + "type": "n8n-nodes-base.httpRequest", + "position": [ + 0, + 0 + ], + "typeVersion": 4.1 + }, + { + "parameters": { + "functionCode": "// Zet deze Code node bij voorkeur op: \"Run once for all items\"\n\nconst get = (obj, path) =>\n path.split('.').reduce((a, k) => (a && a[k] !== undefined ? a[k] : undefined), obj);\n\nconst nz = (v, d = '') => (v === undefined || v === null ? d : v);\nconst ensureArray = (v) => (Array.isArray(v) ? v : v == null ? [] : [v]);\n\nconst cc = (c) => {\n if (!c) return 'NL';\n const u = String(c).toUpperCase();\n return u === 'UK' ? 'GB' : u;\n};\n\nconst xmlEsc = (s) =>\n String(s ?? '')\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n\nconst parseNum = (v) => {\n if (v === undefined || v === null || v === '') return 0;\n if (typeof v === 'number') return v;\n let s = String(v).trim();\n s = s.replace(',', '.');\n const n = Number(s);\n return isNaN(n) ? 0 : n;\n};\n\nconst firstOf = (obj, paths, def = undefined) => {\n for (const p of paths) {\n const val = get(obj, p);\n if (val !== undefined) return val;\n }\n return def;\n};\n\nfunction buildOne(input) {\n const cons = get(input, 'Response.Detail.Data.Manifest.Depot.Account.Consignment') || {};\n const depotRoot = get(input, 'Response.Detail.Data') || {};\n\n const accountName = nz(get(input, 'Response.Detail.Data.Manifest.Depot.Account.Name'), '');\n const number = nz(cons.Number, '');\n\n // Basisvelden\n const trackingId = nz(cons.TrackingID, nz(cons.Reference, ''));\n const reference = nz(cons.Reference, trackingId);\n const weight = parseNum(nz(cons.Weight, 0));\n const dueDate = nz(cons.DueDate, new Date().toISOString().split('T')[0]);\n const dueTime = nz(cons.DueTime, '') === '20:00' ? '' : nz(cons.DueTime, '');\n const driverInfo = nz(cons.ManifestNote, '');\n\n // Adressen\n const addresses = ensureArray(cons.Address);\n const deliveryAddress = addresses.find((a) => /delivery/i.test(a?.Type || '')) || {};\n const collectionAddress = addresses.find((a) => /collection/i.test(a?.Type || '')) || {};\n\n // Paying depot\n const payingDepot = {\n name: nz(\n depotRoot.CompanyName,\n nz(get(input, 'Response.Detail.Data.Manifest.Depot.Account.Name'), 'Palletways')\n ),\n address1: nz(depotRoot.AddressLine1, ''),\n address2: nz(depotRoot.AddressLine2, ''),\n zipcode: nz(depotRoot.Postcode, ''),\n city: nz(depotRoot.Town, ''),\n country: cc(nz(depotRoot.Countrycode, 'NL')),\n phone: nz(depotRoot.Phone, ''),\n email: nz(depotRoot.Email, ''),\n };\n\n // Return/EU => cargo qty1 (leeg als ontbreekt)\n let euroQty1 = '';\n const returnLines = ensureArray(get(cons, 'Return'));\n for (const r of returnLines) {\n const t = String(nz(r?.Type, '')).toUpperCase();\n if (t === 'EU') {\n const raw = r?.Amount;\n if (raw !== undefined && raw !== null && String(raw).trim() !== '') {\n euroQty1 = String(parseNum(raw));\n }\n break;\n }\n }\n\n // cargo unitamount = lifts\n const lifts = parseNum(nz(cons.Lifts, 1)) || 1;\n\n // BookInRequest -> bool2\n const bookInRaw = String(nz(firstOf(cons, ['BookInRequest', 'Delivery.BookInRequest'], ''))).toLowerCase();\n const bool2 = ['yes', 'true', '1', 'y', 'ja'].includes(bookInRaw);\n\n // taillift -> bool1\n const taillift = String(nz(firstOf(cons, ['TailLift', 'Delivery.TailLift'], ''))).toLowerCase();\n const bool1 = ['yes', 'true', '1', 'y', 'ja'].includes(taillift);\n\n // Prijs\n const csvPrice = parseNum(firstOf(input, ['bedrag'], 0));\n\n // ---- Goodsline opbouw ----\n // 1 goodsline per \"stuk\" in BillUnit (Amount), unitamount=1\n const billUnitsRaw = ensureArray(get(cons, 'BillUnit'));\n const billUnits = billUnitsRaw\n .map((bu) => ({\n type: String(nz(bu?.Type, 'HP')).trim(),\n amount: parseNum(nz(bu?.Amount, 0)),\n }))\n .filter((x) => x.type && x.amount > 0);\n\n // Barcodes uit cons.Pallet (kan string of array zijn)\n const palletsArr = ensureArray(get(cons, 'Pallet'))\n .map((x) => String(x || '').trim())\n .filter((x) => x);\n\n // goodsUnits: 1 entry = 1 goodsline\n const goodsUnits = [];\n\n if (billUnits.length > 0) {\n for (const bu of billUnits) {\n let cnt = Math.round(bu.amount);\n if (cnt <= 0) cnt = 1;\n for (let i = 0; i < cnt; i++) {\n goodsUnits.push({ unit_id: bu.type });\n }\n }\n } else if (palletsArr.length > 0) {\n // geen BillUnit, wel pallets => 1 goodsline per pallet-barcode\n for (let i = 0; i < palletsArr.length; i++) goodsUnits.push({ unit_id: 'HP' });\n } else {\n goodsUnits.push({ unit_id: 'HP' });\n }\n\n const totalLines = goodsUnits.length || 1;\n const weightPerLine = totalLines > 0 ? (weight / totalLines) : 0;\n\n // Als goodsLines > lifts: vanaf lifts-index => oversized (geen barcode + goodsline productdescription)\n const hasOversized = totalLines > lifts;\n\n const goods = [];\n for (let i = 0; i < goodsUnits.length; i++) {\n const oversized = hasOversized && i >= lifts;\n\n // Barcode alleen invullen als beschikbaar én niet oversized\n const barcode = (!oversized && palletsArr[i]) ? palletsArr[i] : '';\n\n goods.push({\n seq: i + 1,\n unitamount: 1,\n unit_id: goodsUnits[i].unit_id,\n barcode,\n oversized,\n weight: weightPerLine,\n });\n }\n\n // --------- XML ----------\n const xml = [];\n xml.push('');\n xml.push('');\n xml.push(' ');\n xml.push(' 0');\n xml.push(` ${xmlEsc(trackingId)}`);\n xml.push(' 55107');\n xml.push(' 1');\n xml.push(' 1');\n xml.push(' ');\n xml.push(' ');\n xml.push(` ${xmlEsc(trackingId)}`);\n xml.push(` ${xmlEsc(trackingId)}`);\n xml.push(' 33');\n xml.push(' 55107');\n xml.push(' 1');\n xml.push(` ${xmlEsc(csvPrice)}`);\n\n // sender\n xml.push(' ');\n xml.push(` ${xmlEsc(nz(collectionAddress.CompanyName, ''))}`);\n xml.push(` ${xmlEsc(nz(collectionAddress.Addr1, ''))}`);\n xml.push(` ${xmlEsc(nz(collectionAddress.Addr2, ''))}`);\n xml.push(` ${xmlEsc(nz(collectionAddress.PostCode, ''))}`);\n xml.push(` ${xmlEsc(nz(collectionAddress.Town, ''))}`);\n xml.push(` ${xmlEsc(cc(collectionAddress.Country || 'NL'))}`);\n xml.push(` ${xmlEsc(nz(collectionAddress.ContactName, ''))}`);\n xml.push(` ${xmlEsc(nz(collectionAddress.Telephone, ''))}`);\n xml.push(' ');\n\n // receiver\n xml.push(' ');\n xml.push(` ${xmlEsc(nz(deliveryAddress.CompanyName, ''))}`);\n xml.push(` ${xmlEsc(nz(deliveryAddress.Addr1, ''))}`);\n xml.push(` ${xmlEsc(nz(deliveryAddress.Addr2, ''))}`);\n xml.push(` ${xmlEsc(nz(deliveryAddress.PostCode, ''))}`);\n xml.push(` ${xmlEsc(nz(deliveryAddress.Town, ''))}`);\n xml.push(` ${xmlEsc(cc(deliveryAddress.Country || 'NL'))}`);\n xml.push(` ${xmlEsc(nz(deliveryAddress.ContactName, ''))}`);\n xml.push(` ${xmlEsc(nz(deliveryAddress.Telephone, ''))}`);\n xml.push(' ');\n\n const todayStr = new Date().toISOString().split('T')[0];\n\n // pickupaddress\n xml.push(' ');\n xml.push(' ');\n xml.push(` ${xmlEsc(todayStr)}`);\n xml.push(` ${xmlEsc(todayStr)}`);\n xml.push(' Palletways UK');\n xml.push(' Bijsterhuizen 1103 A');\n xml.push(' 6546 AS');\n xml.push(' Nijmegen');\n xml.push(' NL');\n xml.push(' ');\n\n // deliveryaddress\n xml.push(' ');\n xml.push(' ');\n xml.push(` ${xmlEsc(dueDate)}`);\n xml.push(` ${xmlEsc(dueDate)}`);\n xml.push(` ${xmlEsc(dueTime)}`);\n xml.push(` ${xmlEsc(nz(deliveryAddress.CompanyName, ''))}`);\n xml.push(` ${xmlEsc(nz(deliveryAddress.Addr1, ''))}`);\n xml.push(` ${xmlEsc(nz(deliveryAddress.Addr2, ''))}`);\n xml.push(` ${xmlEsc(nz(deliveryAddress.PostCode, ''))}`);\n xml.push(` ${xmlEsc(nz(deliveryAddress.Town, ''))}`);\n xml.push(` ${xmlEsc(cc(deliveryAddress.Country || 'NL'))}`);\n xml.push(` ${xmlEsc(nz(deliveryAddress.ContactName, ''))}`);\n xml.push(` ${xmlEsc(nz(deliveryAddress.Telephone, ''))}`);\n xml.push(` ${xmlEsc(driverInfo)}`);\n xml.push(' ');\n\n // references\n xml.push(' ');\n xml.push(\n ` 15${xmlEsc(\n trackingId\n )}`\n );\n xml.push(\n ` 16${xmlEsc(\n number\n )}`\n );\n if (accountName) {\n xml.push(\n ` 31${xmlEsc(\n accountName\n )}`\n );\n }\n xml.push(' ');\n\n // parties\n xml.push(' ');\n xml.push(' ');\n xml.push(' 5');\n xml.push(` ${xmlEsc(payingDepot.name)}`);\n xml.push(` ${xmlEsc(payingDepot.address1)}`);\n xml.push(` ${xmlEsc(payingDepot.address2)}`);\n xml.push(` ${xmlEsc(payingDepot.zipcode)}`);\n xml.push(` ${xmlEsc(payingDepot.city)}`);\n xml.push(` ${xmlEsc(payingDepot.country)}`);\n xml.push(` ${xmlEsc(payingDepot.phone)}`);\n xml.push(` ${xmlEsc(payingDepot.email)}`);\n xml.push(' ');\n xml.push(' ');\n\n // cargo\n xml.push(' ');\n xml.push(` ${xmlEsc(lifts)}`);\n xml.push(' 591');\n xml.push(' 123');\n xml.push(' General Cargo');\n xml.push(` ${xmlEsc(weight)}`);\n xml.push(` ${xmlEsc(euroQty1)}`);\n xml.push(` ${bool1 ? 'true' : 'false'}`);\n xml.push(` ${bool2 ? 'true' : 'false'}`);\n xml.push(' ');\n\n for (const g of goods) {\n xml.push(' ');\n xml.push(` ${xmlEsc(g.seq)}`);\n xml.push(` ${xmlEsc(g.unitamount)}`);\n xml.push(` ${xmlEsc(g.unit_id)}`);\n xml.push(` ${xmlEsc(g.weight)}`);\n\n // barcode alleen als aanwezig\n if (g.barcode) {\n xml.push(` ${xmlEsc(g.barcode)}`);\n }\n\n // oversized: productdescription op goodsline\n if (g.oversized) {\n xml.push(` Oversized pallet`);\n }\n\n xml.push(' ');\n }\n\n xml.push(' ');\n xml.push(' ');\n\n xml.push(' ');\n xml.push(' ');\n xml.push(' ');\n xml.push('');\n\n const xmlStr = xml.join('\\n');\n const fileName = trackingId ? `${trackingId}.xml` : `booking_${Date.now()}.xml`;\n\n return { filename: fileName, xml: xmlStr };\n}\n\n// -------- input handling (meerdere zendingen) --------\nlet inputs = [];\nif (typeof $input?.all === 'function') {\n inputs = $input.all().map((i) => i.json);\n} else if (Array.isArray($json)) {\n inputs = $json;\n} else {\n inputs = [$json || {}];\n}\n\n// output: 1 item per consignment\nreturn inputs.map((inp) => ({ json: buildOne(inp) }));" + }, + "id": "7329843a-18b7-42b9-a6b3-2e37cfdedbc8", + "name": "Transform to Transpas Format v3", + "type": "n8n-nodes-base.function", + "typeVersion": 1, + "position": [ + 2208, + 240 + ] + }, + { + "parameters": { + "options": {} + }, + "type": "n8n-nodes-base.splitInBatches", + "typeVersion": 3, + "position": [ + 720, + 176 + ], + "id": "bad74b47-8076-4fd2-a4c0-3e1c74b117d8", + "name": "Loop Over Items" + }, + { + "parameters": { + "protocol": "sftp", + "operation": "upload", + "path": "=/dewit/test/to_tp/delivery_{{ $json.filename }}", + "binaryData": false, + "fileContent": "={{ $json.xml }}", + "options": {} + }, + "type": "n8n-nodes-base.ftp", + "typeVersion": 1, + "position": [ + 2464, + 240 + ], + "id": "bd99d659-63ee-4f66-98ee-f69e96030ee4", + "name": "FTP", + "credentials": { + "sftp": { + "id": "uKyzg5cSXQqXOuHI", + "name": "SFTP n8n" + } + } + }, + { + "parameters": { + "amount": 0.5 + }, + "type": "n8n-nodes-base.wait", + "typeVersion": 1.1, + "position": [ + 2752, + 320 + ], + "id": "a7c6078c-d03a-465e-a5e3-a50b82d2877f", + "name": "Wait", + "webhookId": "aca350c1-7a0b-4a9b-844e-c6be79e44f17" + }, + { + "parameters": { + "rule": { + "interval": [ + { + "field": "cronExpression", + "expression": "0 21 * * 1-5" + } + ] + } + }, + "type": "n8n-nodes-base.scheduleTrigger", + "typeVersion": 1.2, + "position": [ + -288, + 96 + ], + "id": "2cda5c63-4d88-48d7-a46d-0c7b360a6d05", + "name": "Schedule Trigger" + }, + { + "parameters": { + "operation": "deleteRows", + "dataTableId": { + "__rl": true, + "value": "DFLK6lzDC3xNneBe", + "mode": "list", + "cachedResultName": "getobman", + "cachedResultUrl": "/projects/h6yWgTPhk7LTrXDI/datatables/DFLK6lzDC3xNneBe" + }, + "filters": { + "conditions": [ + { + "keyName": "trackingid", + "condition": "isNotEmpty" + } + ] + }, + "options": {} + }, + "type": "n8n-nodes-base.dataTable", + "typeVersion": 1, + "position": [ + 1008, + 16 + ], + "id": "f38c1493-4716-43b4-aa41-b88722804062", + "name": "Delete row(s)" + }, + { + "parameters": { + "operation": "upsert", + "dataTableId": { + "__rl": true, + "value": "DFLK6lzDC3xNneBe", + "mode": "list", + "cachedResultName": "getobman", + "cachedResultUrl": "/projects/h6yWgTPhk7LTrXDI/datatables/DFLK6lzDC3xNneBe" + }, + "filters": { + "conditions": [ + { + "keyName": "trackingid", + "keyValue": "={{ $json.row[0] }}" + } + ] + }, + "columns": { + "mappingMode": "defineBelow", + "value": { + "trackingid": "={{ $json.row[0] }}", + "bedrag": "={{ $json.row[76] }}" + }, + "matchingColumns": [], + "schema": [ + { + "id": "trackingid", + "displayName": "trackingid", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + }, + { + "id": "bedrag", + "displayName": "bedrag", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "readOnly": false, + "removed": false + } + ], + "attemptToConvertTypes": false, + "convertFieldsToString": false + }, + "options": {} + }, + "type": "n8n-nodes-base.dataTable", + "typeVersion": 1, + "position": [ + 512, + 0 + ], + "id": "0071e496-3009-4217-91a7-132fdb546003", + "name": "Upsert row(s)" + }, + { + "parameters": { + "operation": "get", + "dataTableId": { + "__rl": true, + "value": "DFLK6lzDC3xNneBe", + "mode": "list", + "cachedResultName": "getobman", + "cachedResultUrl": "/projects/h6yWgTPhk7LTrXDI/datatables/DFLK6lzDC3xNneBe" + }, + "filters": { + "conditions": [ + { + "keyName": "trackingid", + "keyValue": "={{ $json.trackingid }}" + } + ] + } + }, + "type": "n8n-nodes-base.dataTable", + "typeVersion": 1, + "position": [ + 1424, + 336 + ], + "id": "18f6bde1-aaf1-4c49-91f9-846b160fc409", + "name": "Get row(s)", + "alwaysOutputData": true + }, + { + "parameters": { + "url": "=https://portal.palletways.com/api/getObman/{{ $json.lastWorkday }}?apikey=&output=xml", + "sendQuery": true, + "queryParameters": { + "parameters": [ + { + "name": "apikey", + "value": "SXJaM7PLjZDqfsnXSDP8Y9wDY6crxJySBg705MQEPus%3D" + }, + { + "name": "date", + "value": "={{ $json[\"lastWorkday\"] }}" + }, + { + "name": "output", + "value": "xml" + } + ] + }, + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + 208, + 176 + ], + "id": "8e44181f-ebe3-43f0-a54f-9000bbac8c5b", + "name": "HTTP Request" + }, + { + "parameters": { + "functionCode": "// ---------- helpers ----------\nconst get = (obj, path) =>\n path.split('.').reduce((a, k) => (a && a[k] !== undefined ? a[k] : undefined), obj);\n\nconst nz = (v, d = '') => (v === undefined || v === null ? d : v);\nconst ensureArray = (v) => (Array.isArray(v) ? v : v == null ? [] : [v]);\n\nconst cc = (c) => {\n if (!c) return 'NL';\n const u = String(c).toUpperCase();\n return u === 'UK' ? 'GB' : u;\n};\n\nconst xmlEsc = (s) =>\n String(s ?? '')\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n\nconst parseNum = (v) => {\n if (v === undefined || v === null || v === '') return 0;\n if (typeof v === 'number') return v;\n\n // Spaties weg\n let s = String(v).trim();\n\n // Komma naar punt\n s = s.replace(',', '.');\n\n const n = Number(s);\n return isNaN(n) ? 0 : n;\n};\n\nconst firstOf = (obj, paths, def = undefined) => {\n for (const p of paths) {\n const val = get(obj, p);\n if (val !== undefined) return val;\n }\n return def;\n};\n\n// ---- bron ----\nconst input = $json || {};\nconst cons = get(input, 'Response.Detail.Data.Manifest.Depot.Account.Consignment') || {};\nconst depotRoot = get(input, 'Response.Detail.Data') || {};\n\n// Basisvelden\nconst trackingId = nz(cons.TrackingID, nz(cons.Reference, ''));\nconst reference = nz(cons.Reference, trackingId);\nconst weight = parseNum(nz(cons.Weight, 0));\nconst dueDate = nz(cons.DueDate, new Date().toISOString().split('T')[0]);\nconst dueTime = nz(cons.DueTime, '');\nconst driverInfo = nz(cons.ManifestNote, '');\n\n// Adressen\nconst addresses = ensureArray(cons.Address);\nconst deliveryAddress = addresses.find((a) => /delivery/i.test(a?.Type || '')) || {};\nconst collectionAddress = addresses.find((a) => /collection/i.test(a?.Type || '')) || {};\n\n// Paying depot\nconst payingDepot = {\n name: nz(\n depotRoot.CompanyName,\n nz(get(input, 'Response.Detail.Data.Manifest.Depot.Account.Name'), 'Palletways')\n ),\n address1: nz(depotRoot.AddressLine1, ''),\n address2: nz(depotRoot.AddressLine2, ''),\n zipcode: nz(depotRoot.Postcode, ''),\n city: nz(depotRoot.Town, ''),\n country: cc(nz(depotRoot.Countrycode, 'NL')),\n phone: nz(depotRoot.Phone, ''),\n email: nz(depotRoot.Email, ''),\n};\n\n// --- Goodsline ---\nlet goods = [];\n\nconst palletDetail = get(cons, 'PalletDetail');\nif (palletDetail) {\n // gebruik PalletDetail\n goods.push({\n seq: 1,\n unitamount: 1,\n unit_id: nz(palletDetail.PalletType, 'HP'),\n weight: parseNum(palletDetail.Weight || weight),\n length: parseNum(palletDetail.Length || 120),\n width: parseNum(palletDetail.Width || 80),\n height: parseNum(palletDetail.Height || 150),\n });\n} else {\n // BillUnits\n const billUnits = ensureArray(cons.BillUnit);\n let seq = 1;\n for (const bu of billUnits) {\n const amount = parseNum(bu.Amount || 0);\n if (amount > 0) {\n goods.push({\n seq: seq++,\n unitamount: amount,\n unit_id: nz(bu.Type, 'HP'),\n weight: weight > 0 ? weight / amount : 0,\n length: 120,\n width: 80,\n height: 150,\n });\n }\n }\n // fallback\n if (goods.length === 0) {\n goods.push({\n seq: 1,\n unitamount: 1,\n unit_id: 'HP',\n weight: weight,\n length: 120,\n width: 80,\n height: 150,\n });\n }\n}\n\n// BookInRequest -> bool2\nconst bookInRaw = String(nz(firstOf(cons, ['BookInRequest', 'Delivery.BookInRequest'], ''))).toLowerCase();\nconst bool2 = ['yes', 'true', '1', 'y', 'ja'].includes(bookInRaw);\n\n// taillift -> bool1\nconst taillift = String(nz(firstOf(cons, ['TailLift', 'Delivery.TailLift'], ''))).toLowerCase();\nconst bool1 = ['yes', 'true', '1', 'y', 'ja'].includes(taillift);\n\n// Prijs\nconst csvPrice = parseNum(firstOf(input, ['bedrag'], 0));\n\n// --------- XML ----------\nconst xml = [];\nxml.push('');\nxml.push('');\nxml.push(' ');\nxml.push(' 0');\nxml.push(` ${xmlEsc(trackingId)}`);\nxml.push(' 55107');\nxml.push(' 1');\nxml.push(' 1');\nxml.push(' ');\nxml.push(' ');\nxml.push(` ${xmlEsc(trackingId)}`);\nxml.push(` ${xmlEsc(trackingId)}`);\nxml.push(' 33');\nxml.push(' 55107');\nxml.push(' 1');\nxml.push(` ${xmlEsc(csvPrice)}`);\n\n// sender\nxml.push(' ');\nxml.push(` ${xmlEsc(nz(collectionAddress.CompanyName, ''))}`);\nxml.push(` ${xmlEsc(nz(collectionAddress.Addr1, ''))}`);\nxml.push(` ${xmlEsc(nz(collectionAddress.Addr2, ''))}`);\nxml.push(` ${xmlEsc(nz(collectionAddress.PostCode, ''))}`);\nxml.push(` ${xmlEsc(nz(collectionAddress.Town, ''))}`);\nxml.push(` ${xmlEsc(cc(collectionAddress.Country || 'NL'))}`);\nxml.push(` ${xmlEsc(nz(collectionAddress.ContactName, ''))}`);\nxml.push(` ${xmlEsc(nz(collectionAddress.Telephone, ''))}`);\nxml.push(' ');\n\n// receiver\nxml.push(' ');\nxml.push(` ${xmlEsc(nz(deliveryAddress.CompanyName, ''))}`);\nxml.push(` ${xmlEsc(nz(deliveryAddress.Addr1, ''))}`);\nxml.push(` ${xmlEsc(nz(deliveryAddress.Addr2, ''))}`);\nxml.push(` ${xmlEsc(nz(deliveryAddress.PostCode, ''))}`);\nxml.push(` ${xmlEsc(nz(deliveryAddress.Town, ''))}`);\nxml.push(` ${xmlEsc(cc(deliveryAddress.Country || 'NL'))}`);\nxml.push(` ${xmlEsc(nz(deliveryAddress.ContactName, ''))}`);\nxml.push(` ${xmlEsc(nz(deliveryAddress.Telephone, ''))}`);\nxml.push(' ');\n\nconst todayStr = new Date().toISOString().split('T')[0];\n// pickupaddress\nxml.push(' ');\nxml.push(` ${xmlEsc(todayStr)}`);\nxml.push(` ${xmlEsc(todayStr)}`);\nxml.push(' Palletways UK');\nxml.push(' Bijsterhuizen 1103 A');\nxml.push(' 6546 AS');\nxml.push(' Nijmegen');\nxml.push(' NL');\nxml.push(' ');\n\n// deliveryaddress\nxml.push(' ');\nxml.push(' ');\nxml.push(` ${xmlEsc(dueDate)}`);\nxml.push(` ${xmlEsc(dueDate)}`);\nxml.push(` ${xmlEsc(dueTime)}`);\nxml.push(` ${xmlEsc(nz(deliveryAddress.CompanyName, ''))}`);\nxml.push(` ${xmlEsc(nz(deliveryAddress.Addr1, ''))}`);\nxml.push(` ${xmlEsc(nz(deliveryAddress.Addr2, ''))}`);\nxml.push(` ${xmlEsc(nz(deliveryAddress.PostCode, ''))}`);\nxml.push(` ${xmlEsc(nz(deliveryAddress.Town, ''))}`);\nxml.push(` ${xmlEsc(cc(deliveryAddress.Country || 'NL'))}`);\nxml.push(` ${xmlEsc(nz(deliveryAddress.ContactName, ''))}`);\nxml.push(` ${xmlEsc(nz(deliveryAddress.Telephone, ''))}`);\nxml.push(` ${xmlEsc(driverInfo)}`);\nxml.push(' ');\n\n// references\nxml.push(' ');\nxml.push(\n ` 15${xmlEsc(\n trackingId\n )}`\n);\nxml.push(\n ` 16${xmlEsc(\n reference\n )}`\n);\nxml.push(' ');\n\n// parties\nxml.push(' ');\nxml.push(' ');\nxml.push(` 5`);\nxml.push(` ${xmlEsc(payingDepot.name)}`);\nxml.push(` ${xmlEsc(payingDepot.address1)}`);\nxml.push(` ${xmlEsc(payingDepot.address2)}`);\nxml.push(` ${xmlEsc(payingDepot.zipcode)}`);\nxml.push(` ${xmlEsc(payingDepot.city)}`);\nxml.push(` ${xmlEsc(payingDepot.country)}`);\nxml.push(` ${xmlEsc(payingDepot.phone)}`);\nxml.push(` ${xmlEsc(payingDepot.email)}`);\nxml.push(' ');\nxml.push(' ');\n\n// cargo\nxml.push(' ');\nxml.push(' 1');\nxml.push(' 591');\nxml.push(' 123');\nxml.push(' General Cargo');\nxml.push(` ${xmlEsc(weight)}`);\nxml.push(` ${bool1 ? 'true' : 'false'}`);\nxml.push(` ${bool2 ? 'true' : 'false'}`);\nxml.push(' ');\nfor (const g of goods) {\n xml.push(' ');\n xml.push(` ${xmlEsc(g.seq)}`);\n xml.push(` ${xmlEsc(g.unitamount)}`);\n xml.push(` ${xmlEsc(g.unit_id)}`);\n xml.push(` ${xmlEsc(g.weight)}`);\n xml.push(` ${xmlEsc(g.length)}`);\n xml.push(` ${xmlEsc(g.width)}`);\n xml.push(` ${xmlEsc(g.height)}`);\n xml.push(' ');\n}\nxml.push(' ');\nxml.push(' ');\n\nxml.push(' ');\nxml.push(' ');\nxml.push(' ');\nxml.push('');\n\nconst xmlStr = xml.join('\\n');\nconst fileName = trackingId ? `${trackingId}.xml` : `booking_${Date.now()}.xml`;\n\nreturn [\n {\n json: {\n filename: fileName,\n xml: xmlStr,\n },\n },\n];\n" + }, + "id": "0bdffb34-d13d-459e-b953-10a558a77dcd", + "name": "Transform to Transpas Format v", + "type": "n8n-nodes-base.function", + "typeVersion": 1, + "position": [ + 2144, + -512 + ] + }, + { + "parameters": { + "content": "## Outbound manifest wordt gebruikt voor deze flow", + "width": 352 + }, + "type": "n8n-nodes-base.stickyNote", + "typeVersion": 1, + "position": [ + 80, + -208 + ], + "id": "7e6030ff-95b0-4ec7-a087-5ba496ce5876", + "name": "Sticky Note" + } + ], + "pinData": {}, + "connections": { + "Schedule Trigger": { + "main": [ + [ + { + "node": "Download CSV", + "type": "main", + "index": 0 + }, + { + "node": "Last workday1", + "type": "main", + "index": 0 + } + ] + ] + }, + "Download CSV": { + "main": [ + [ + { + "node": "Import CSV", + "type": "main", + "index": 0 + } + ] + ] + }, + "Import CSV": { + "main": [ + [ + { + "node": "Upsert row(s)", + "type": "main", + "index": 0 + } + ] + ] + }, + "Last workday1": { + "main": [ + [ + { + "node": "HTTP Request", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code": { + "main": [ + [ + { + "node": "Loop Over Items", + "type": "main", + "index": 0 + } + ] + ] + }, + "Loop Over Items": { + "main": [ + [ + { + "node": "Delete row(s)", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Get Consigment", + "type": "main", + "index": 0 + }, + { + "node": "Get row(s)", + "type": "main", + "index": 0 + } + ] + ] + }, + "Get Consigment": { + "main": [ + [ + { + "node": "Parse XML Response", + "type": "main", + "index": 0 + } + ] + ] + }, + "Get Paying Depot Address": { + "main": [ + [ + { + "node": "Parse Paying Depot XML", + "type": "main", + "index": 0 + } + ] + ] + }, + "Parse XML Response": { + "main": [ + [ + { + "node": "Merge", + "type": "main", + "index": 1 + }, + { + "node": "Get Paying Depot Address", + "type": "main", + "index": 0 + } + ] + ] + }, + "Parse Paying Depot XML": { + "main": [ + [ + { + "node": "Merge", + "type": "main", + "index": 0 + } + ] + ] + }, + "Merge": { + "main": [ + [ + { + "node": "Transform to Transpas Format v3", + "type": "main", + "index": 0 + } + ] + ] + }, + "Transform to Transpas Format v3": { + "main": [ + [ + { + "node": "FTP", + "type": "main", + "index": 0 + } + ] + ] + }, + "FTP": { + "main": [ + [ + { + "node": "Wait", + "type": "main", + "index": 0 + } + ] + ] + }, + "Wait": { + "main": [ + [ + { + "node": "Loop Over Items", + "type": "main", + "index": 0 + } + ] + ] + }, + "Get row(s)": { + "main": [ + [ + { + "node": "Merge", + "type": "main", + "index": 2 + } + ] + ] + }, + "HTTP Request": { + "main": [ + [ + { + "node": "Code", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1", + "binaryMode": "separate", + "timezone": "Europe/Amsterdam", + "callerPolicy": "workflowsFromSameOwner", + "availableInMCP": false + }, + "versionId": "d069831f-6a06-4f66-aecc-ae1d79985359", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "bef8d409866a58c0777dfe7cca1b9c2400fd051c056d361501393ab423006b5f" + }, + "nodeGroups": [], + "id": "qDrWrANHJWpTQm6P", + "tags": [ + { + "updatedAt": "2025-11-11T04:59:39.416Z", + "createdAt": "2025-11-11T04:59:39.416Z", + "id": "ClOZrAjJKoGLFvaO", + "name": "Palletways" + }, + { + "updatedAt": "2025-11-09T14:44:19.471Z", + "createdAt": "2025-11-09T14:44:19.471Z", + "id": "DHSMO2U01lFZRZ27", + "name": "Productie" + } + ] +} \ No newline at end of file diff --git a/palletways/Pre-Alert Kwetsbare Zending.json b/palletways/Pre-Alert Kwetsbare Zending.json new file mode 100644 index 0000000..49a8559 --- /dev/null +++ b/palletways/Pre-Alert Kwetsbare Zending.json @@ -0,0 +1,182 @@ +{ + "name": "Pre-Alert Kwetsbare Zending", + "nodes": [ + { + "parameters": { + "path": "prealert", + "responseMode": "responseNode", + "options": {} + }, + "id": "06751456-f404-420f-aebc-559b903b6263", + "name": "Incoming Data", + "type": "n8n-nodes-base.webhook", + "typeVersion": 1, + "position": [ + 256, + -112 + ], + "webhookId": "24cbeb29-a571-4e64-affd-3e9149032552" + }, + { + "parameters": { + "keepOnlySet": true, + "values": { + "string": [ + { + "name": "html", + "value": "=

Pre-alert: Kwetsbare zending voor
{{$json[\"customerCompany\"]}}

Beste,

Graag willen wij u bij deze informeren over een aankomende zending voor onze klant {{$json[\"customerCompany\"]}}, waarvoor wij uw extra aandacht vragen.

De zending bestaat uit kwetsbare goederen die gevoelig zijn voor beschadiging bij onzorgvuldige behandeling. Wij verzoeken u daarom vriendelijk om tijdens het transport en de overslag extra zorgvuldigheid in acht te nemen.

Details van de zending:

Klant:{{$json[\"customerName\"]}}
Bestemming:{{$json[\"destination\"]}}
DueDate:{{$json[\"dueDate\"]}}
Aantal colli/pallets:{{$json[\"palletCount\"]}}
Type goederen:{{$json[\"goodsType\"]}}
Afmeting (LxBxH):{{$json[\"dimensions\"]}}
TrackingID:{{$json[\"trackingId\"]}}

Wij vertrouwen erop dat u deze zending met de nodige zorg zult behandelen. Mocht u vragen hebben of aanvullende informatie nodig hebben, neem dan gerust contact met ons op.

Alvast bedankt voor uw medewerking.

Met vriendelijke groet,
{{$json[\"senderName\"]}}
{{$json[\"senderCompany\"]}}

" + }, + { + "name": "subject", + "value": "=Pre-alert kwetsbare zending {{ $json.trackingId }}" + } + ] + }, + "options": {} + }, + "id": "3adeef74-f3ca-4045-9c8d-f2a41b6c8555", + "name": "Set HTML Email", + "type": "n8n-nodes-base.set", + "typeVersion": 2, + "position": [ + 672, + 64 + ] + }, + { + "parameters": { + "mode": "raw", + "jsonOutput": "{\n \"customerCompany\": \"KamadoBBQ B.V.\",\n \"customerName\": \"MDN\",\n \"destination\": \"Konijnenberg 189\",\n \"dueDate\": \"23-01-2026\",\n \"palletCount\": \"1\",\n \"goodsType\": \"HOUTEN PALLET - The Bastard Urban Large Complete BBQ\",\n \"dimensions\": \"120 x 80 x 2200\",\n \"trackingId\": \"60044640865\",\n \"senderName\": \"Team Palletways\",\n \"senderCompany\": \"De Wit Transport\"\n}\n", + "options": {} + }, + "type": "n8n-nodes-base.set", + "typeVersion": 3.4, + "position": [ + 480, + 64 + ], + "id": "ff91d20a-1c41-4075-94c4-186d2a2db791", + "name": "Edit Fields" + }, + { + "parameters": {}, + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 272, + 64 + ], + "id": "a12760e0-de99-4a2d-a421-ba084b095ae2", + "name": "When clicking ‘Execute workflow’" + }, + { + "parameters": { + "respondWith": "text", + "responseBody": "ok", + "options": {} + }, + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.4, + "position": [ + 624, + -112 + ], + "id": "2e366906-a7ff-489f-9bca-853d2a196f59", + "name": "Respond to Webhook" + }, + { + "parameters": { + "toRecipients": "jeroen@dmsmail.nl", + "subject": "={{ $json.subject }}", + "bodyContent": "={{ $json.html }}", + "additionalFields": { + "bodyContentType": "html" + } + }, + "type": "n8n-nodes-base.microsoftOutlook", + "typeVersion": 2, + "position": [ + 944, + 64 + ], + "id": "29cddd6a-dfd9-41e9-86ba-10366b109b1d", + "name": "Send a message1", + "webhookId": "54154bc4-07a8-4245-a82e-01b148dcbb88", + "credentials": { + "microsoftOutlookOAuth2Api": { + "id": "PGANiL6y4sGEEvao", + "name": "Microsoft Outlook Palletways" + } + } + } + ], + "pinData": {}, + "connections": { + "Incoming Data": { + "main": [ + [ + { + "node": "Edit Fields", + "type": "main", + "index": 0 + } + ] + ] + }, + "Set HTML Email": { + "main": [ + [ + { + "node": "Send a message1", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields": { + "main": [ + [ + { + "node": "Set HTML Email", + "type": "main", + "index": 0 + }, + { + "node": "Respond to Webhook", + "type": "main", + "index": 0 + } + ] + ] + }, + "When clicking ‘Execute workflow’": { + "main": [ + [ + { + "node": "Edit Fields", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1", + "binaryMode": "separate", + "timeSavedMode": "fixed", + "timezone": "Europe/Amsterdam", + "callerPolicy": "workflowsFromSameOwner", + "availableInMCP": false + }, + "versionId": "37a34db6-35aa-4f51-add9-0285d2784f25", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "bef8d409866a58c0777dfe7cca1b9c2400fd051c056d361501393ab423006b5f" + }, + "nodeGroups": [], + "id": "dYaxybPLBh4EoljA", + "tags": [] +} \ No newline at end of file diff --git a/palletways/pw prijzen.json b/palletways/pw prijzen.json new file mode 100644 index 0000000..65fca66 --- /dev/null +++ b/palletways/pw prijzen.json @@ -0,0 +1,348 @@ +{ + "name": "pw prijzen", + "nodes": [ + { + "parameters": { + "formTitle": "Palletways tarief bereken", + "formFields": { + "values": [ + { + "fieldLabel": "Van postcode", + "placeholder": "ColPostCode", + "defaultValue": "2181vh" + }, + { + "fieldLabel": "ColCountryCode", + "fieldType": "dropdown", + "defaultValue": "NL", + "fieldOptions": { + "values": [ + { + "option": "NL" + }, + { + "option": "DE" + }, + { + "option": "IT" + }, + { + "option": "BE" + }, + { + "option": "ES" + }, + { + "option": "FR" + } + ] + } + }, + { + "fieldLabel": "Naar postcode", + "placeholder": "DelPostCode", + "defaultValue": "2181VH" + }, + { + "fieldLabel": "Naar land", + "fieldType": "dropdown", + "defaultValue": "NL", + "fieldOptions": { + "values": [ + { + "option": "NL" + }, + { + "option": "DE" + }, + { + "option": "BE" + }, + { + "option": "IT" + }, + { + "option": "ES" + }, + { + "option": "FR" + } + ] + } + }, + { + "fieldLabel": "Lifts", + "fieldType": "number", + "placeholder": "Lifts", + "defaultValue": "1" + }, + { + "fieldLabel": "Gewicht", + "fieldType": "number", + "placeholder": "Gewicht", + "defaultValue": "700" + }, + { + "fieldLabel": "Mini Quarter Pallet", + "fieldType": "number", + "placeholder": "mqp", + "defaultValue": "0" + }, + { + "fieldLabel": "Quarter Pallet", + "fieldType": "number", + "placeholder": "qp", + "defaultValue": "0" + }, + { + "fieldLabel": "Half Pallet", + "fieldType": "number", + "placeholder": "hp", + "defaultValue": "0" + }, + { + "fieldLabel": "Euro pallet light", + "fieldType": "number", + "placeholder": "epl", + "defaultValue": "0" + }, + { + "fieldLabel": "Light Pallet", + "fieldType": "number", + "placeholder": "lp", + "defaultValue": "0" + }, + { + "fieldLabel": "Full Pallet", + "fieldType": "number", + "placeholder": "fp", + "defaultValue": "0" + }, + { + "fieldLabel": "shuttleCost", + "placeholder": "shuttleCost", + "defaultValue": "20" + }, + { + "fieldLabel": "marginPct", + "placeholder": "marginPct", + "defaultValue": "20" + }, + { + "fieldLabel": "dotPct", + "placeholder": "dotPct", + "defaultValue": "5" + } + ] + }, + "options": {} + }, + "type": "n8n-nodes-base.formTrigger", + "typeVersion": 2.3, + "position": [ + -432, + -96 + ], + "id": "6fb1d884-a8eb-42e6-9cce-51e52fcb322f", + "name": "On form submission", + "webhookId": "7fbd25a6-977d-4db6-878a-99e53ce33352" + }, + { + "parameters": { + "mode": "raw", + "jsonOutput": "={\n \"QuotationRequest\": {\n \"Date\": \"2025-12-21\",\n \"Time\": \"20:50\",\n \"Account\": { \"Code\": \"100000\" },\n \"Consignment\": {\n \"Type\": \"3\",\n \"Classification\": \"B2B\",\n \"ColCountryCode\": \"{{ $json.ColCountryCode }}\",\n \"ColPostCode\": \"{{ $json['Van postcode'] }}\",\n \"DelCountryCode\": \"{{ $json['Naar land'] }}\",\n \"DelPostCode\": \"{{ $json['Naar postcode'] }}\",\n \"Insurance\": \"standard\",\n \"Lifts\": {{ $json.Lifts }},\n \"Weight\": {{ $json.Gewicht }},\n \"BillUnit\": [\n { \"Type\": \"FP\", \"Amount\": {{ $json['Full Pallet'] }} },\n { \"Type\": \"LP\", \"Amount\": {{ $json['Light Pallet'] }} },\n { \"Type\": \"EPL\", \"Amount\": {{ $json['Euro pallet light'] }} },\n { \"Type\": \"QP\", \"Amount\": {{ $json['Quarter Pallet'] }} },\n { \"Type\": \"MQP\", \"Amount\": {{ $json['Mini Quarter Pallet'] }} }\n ],\n \"Return\": [\n { \"Type\": \"EU\", \"Amount\": 0 },\n { \"Type\": \"EG\", \"Amount\": 0 }\n ],\n \"Handball\": false,\n \"TailLift\": false,\n \"BookInRequest\": false\n }\n }\n }\n", + "options": {} + }, + "type": "n8n-nodes-base.set", + "typeVersion": 3.4, + "position": [ + -192, + -96 + ], + "id": "23edf830-f280-42c2-b70f-60371afe2a2b", + "name": "Edit Fields" + }, + { + "parameters": { + "mode": "jsonToxml", + "options": {} + }, + "type": "n8n-nodes-base.xml", + "typeVersion": 1, + "position": [ + 16, + -96 + ], + "id": "e9b81be0-73d3-447f-afb4-c09f317e5aeb", + "name": "XML" + }, + { + "parameters": { + "method": "POST", + "url": "https://api.palletways.com/getdepotconsignmentquotations?apikey=SXJaM7PLjZDqfsnXSDP8Y9wDY6crxJySBg705MQEPus%3D&inputformat=xml&outputformat=json", + "sendBody": true, + "contentType": "form-urlencoded", + "bodyParameters": { + "parameters": [ + { + "name": "data", + "value": "={{ $json.data }}" + } + ] + }, + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.3, + "position": [ + 256, + -96 + ], + "id": "1bd888b1-6c57-4f06-b920-298f3d136ba0", + "name": "HTTP Request1", + "onError": "continueRegularOutput" + }, + { + "parameters": { + "operation": "completion", + "respondWith": "showText", + "responseText": "={{ $json.formSubmittedText }}" + }, + "type": "n8n-nodes-base.form", + "typeVersion": 2.4, + "position": [ + 896, + -144 + ], + "id": "6df73b0d-329a-4a95-9851-3623cb7af4ee", + "name": "Form", + "webhookId": "e1674065-c8d5-4ca3-aae6-9fd2a13ea847" + }, + { + "parameters": { + "jsCode": "const root = $input.first().json;\n\nconst quotations =\n root?.Detail?.Data?.[0]?.Hub?.Quotations?.Quotation ?? [];\n\nreturn quotations.map((q) => ({\n json: {\n colSurchargeName: q?.ColService?.SurchargeName ?? null,\n delSurchargeName: q?.DelService?.SurchargeName ?? null,\n chargeTotal: q?.ChargeTotal ?? null,\n chargeCurrency: q?.ChargeCurrency ?? null, // extra handig\n colServiceName: q?.ColService?.ServiceName ?? null, // extra handig\n delServiceName: q?.DelService?.ServiceName ?? null, // extra handig\n },\n}));\n" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 464, + -96 + ], + "id": "06814b2e-4508-4995-bacd-683ab7a8472e", + "name": "Code in JavaScript" + }, + { + "parameters": { + "jsCode": "const rows = $input.all().map(i => i.json);\n\nconst esc = (s) => String(s ?? '').replace(//g, '>');\n\nlet html = `Offertes

`;\nhtml += `Col surcharge | Del surcharge | Total
`;\nhtml += `--- | --- | ---
`;\n\nhtml += rows\n .map(r =>\n `${esc(r.colSurchargeName)} | ${esc(r.delSurchargeName)} | ${esc(r.chargeTotal)} ${esc(r.chargeCurrency ?? '')}`\n )\n .join('
');\n\nreturn [\n {\n json: {\n formSubmittedText: html,\n },\n },\n];\n" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 672, + -96 + ], + "id": "f5daf975-6102-4c24-8c32-3191e5ab90a8", + "name": "Code in JavaScript1" + }, + { + "parameters": { + "jsCode": "const data = $input.first().json;\n\n// Pak de API response (robust: werkt of hij nou op root staat of in body/data)\nconst resp =\n data?.Detail ? data :\n data?.body?.Detail ? data.body :\n data?.data?.Detail ? data.data :\n data;\n\n// Quotations array\nconst quotations =\n resp?.Detail?.Data?.[0]?.Hub?.Quotations?.Quotation ?? [];\n\nconst toNum = (v) => {\n const n = Number(String(v ?? \"\").replace(\",\", \".\"));\n return Number.isFinite(n) ? n : 0;\n};\n\n// pallets = lifts\nconst pallets = Math.max(1, Math.round(toNum(data.lifts ?? data.pallets ?? 1)));\n\n// shuttleCost is PER pallet\nconst shuttlePerPallet = toNum(data.shuttleCost);\n\n// percentages\nconst marginPct = toNum(data.marginPct); // bv 12 = 12%\nconst dotPct = toNum(data.dotPct); // bv 5 = 5%\n\n// goedkoopste quote\nconst cheapest = quotations.reduce((best, q) => {\n if (!best) return q;\n return toNum(q.ChargeTotal) < toNum(best.ChargeTotal) ? q : best;\n}, null);\n\n// basis charge: user override of goedkoopste ChargeTotal\nconst hasChargeOverride =\n data.charge !== undefined && data.charge !== null && String(data.charge).trim() !== \"\";\n\nconst baseCharge = hasChargeOverride\n ? toNum(data.charge)\n : (cheapest ? toNum(cheapest.ChargeTotal) : 0);\n\n// === BEREKENING ===\nconst shuttleTotal = shuttlePerPallet * pallets;\n\nconst costExcl = baseCharge + shuttleTotal;\nconst dotAmount = costExcl * (dotPct / 100);\nconst costInclDot = costExcl + dotAmount;\n\nconst tariff = costInclDot * (1 + marginPct / 100);\nconst marginAmount = tariff - costInclDot;\n\nconst fmt = (n) => n.toFixed(2);\n\nconst esc = (s) =>\n String(s ?? \"\")\n .replace(/&/g, \"&\")\n .replace(//g, \">\");\n\nlet html = `Offertes (Palletways)
`;\n\nif (!quotations.length) {\n html += `Geen quotes gevonden in response.
`;\n} else {\n html += `
Quotes
`;\n  html += `#  | Col surcharge                 | Del surcharge                 | Total\\n`;\n  html += `---+------------------------------+------------------------------+--------\\n`;\n\n  quotations.slice(0, 25).forEach((q, idx) => {\n    const col = esc(q?.ColService?.SurchargeName ?? \"\");\n    const del = esc(q?.DelService?.SurchargeName ?? \"\");\n    const total = esc(q?.ChargeTotal ?? \"\");\n    const mark = (cheapest === q) ? \"*\" : \" \";\n    html += `${mark}${String(idx + 1).padStart(2, \" \")} | ${col.slice(0, 28).padEnd(28)} | ${del.slice(0, 28).padEnd(28)} | ${total}\\n`;\n  });\n\n  html += `
* = geselecteerd (laagste ChargeTotal)
`;\n}\n\nhtml += `
Berekening
`;\nhtml += `Lifts/Pallets: ${pallets}
`;\nhtml += `Basis charge: € ${fmt(baseCharge)}
`;\nhtml += `Shuttle per pallet: € ${fmt(shuttlePerPallet)} → totaal € ${fmt(shuttleTotal)}
`;\nhtml += `DOT (${fmt(dotPct)}%): € ${fmt(dotAmount)}
`;\nhtml += `Kosten incl. DOT: € ${fmt(costInclDot)}
`;\nhtml += `Totaal tarief: € ${fmt(tariff)}
`;\nhtml += `Marge (wat je overhoudt): € ${fmt(marginAmount)}
`;\nhtml += `Tarief per pallet: € ${fmt(tariff / pallets)}
`;\n\nreturn [{ json: { formSubmittedText: html } }];\n" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 464, + -320 + ], + "id": "ff2c8cbf-336c-4bdf-ac49-08fa94b9d566", + "name": "Code in JavaScript2" + } + ], + "pinData": {}, + "connections": { + "On form submission": { + "main": [ + [ + { + "node": "Edit Fields", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields": { + "main": [ + [ + { + "node": "XML", + "type": "main", + "index": 0 + } + ] + ] + }, + "XML": { + "main": [ + [ + { + "node": "HTTP Request1", + "type": "main", + "index": 0 + } + ] + ] + }, + "HTTP Request1": { + "main": [ + [ + { + "node": "Code in JavaScript2", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code in JavaScript": { + "main": [ + [ + { + "node": "Code in JavaScript1", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code in JavaScript1": { + "main": [ + [] + ] + }, + "Code in JavaScript2": { + "main": [ + [ + { + "node": "Form", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1", + "binaryMode": "separate" + }, + "versionId": "57bec1f7-e133-4e0d-b04f-f53a492772fe", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "bef8d409866a58c0777dfe7cca1b9c2400fd051c056d361501393ab423006b5f" + }, + "nodeGroups": [], + "id": "JAv6gwnDzNjkHkRz", + "tags": [] +} \ No newline at end of file