diff --git a/Lommerse email.json b/Lommerse email.json
new file mode 100644
index 0000000..279d17c
--- /dev/null
+++ b/Lommerse email.json
@@ -0,0 +1,361 @@
+{
+ "name": "Lommerse email",
+ "nodes": [
+ {
+ "parameters": {
+ "operation": "xml",
+ "options": {}
+ },
+ "type": "n8n-nodes-base.extractFromFile",
+ "typeVersion": 1.1,
+ "position": [
+ 592,
+ -192
+ ],
+ "id": "32cc76af-2abc-43b6-8e06-ca4e8ab1cc60",
+ "name": "Extract from File"
+ },
+ {
+ "parameters": {
+ "assignments": {
+ "assignments": [
+ {
+ "id": "d56701fc-95ba-46f3-8e1f-5e5fcc23a97f",
+ "name": "LoadingDate",
+ "value": "={{ $json.LoadingDate }}",
+ "type": "string"
+ },
+ {
+ "id": "8fb36d6e-f61f-4cfd-8f22-88e303d1a606",
+ "name": "LoadingPlace",
+ "value": "={{ $json.LoadingPlace }}",
+ "type": "string"
+ },
+ {
+ "id": "33ad14dc-de29-442d-8201-4200e9b56da0",
+ "name": "Cargonr",
+ "value": "={{ $json.Cargonr }}",
+ "type": "number"
+ },
+ {
+ "id": "83d62b1a-eae8-4b5d-a7df-836509287d34",
+ "name": "LoadingTripID",
+ "value": "={{ $json.LoadingTripID }}",
+ "type": "number"
+ },
+ {
+ "id": "897ae901-02d7-4b9c-a180-5f8e93dd67fd",
+ "name": "LoadingTruck",
+ "value": "={{ $json.LoadingTruck }}",
+ "type": "string"
+ },
+ {
+ "id": "112ae60f-081d-4d65-83f6-4bf93d30600b",
+ "name": "UnloadingPlace",
+ "value": "={{ $json.UnloadingPlace }}",
+ "type": "string"
+ },
+ {
+ "id": "1bd8e2ca-3c95-4a8e-8592-54f99cbfbbf0",
+ "name": "Cargo_UnitAmount",
+ "value": "={{ $json.Cargo_UnitAmount }}",
+ "type": "number"
+ },
+ {
+ "id": "ec312e85-67a9-4dbe-9fb3-6173ae2da9a6",
+ "name": "Cargo_UnitDescription",
+ "value": "={{ $json.Cargo_UnitDescription }}",
+ "type": "string"
+ },
+ {
+ "id": "2a245dfb-4d8e-4276-b5b3-64fb50eb0115",
+ "name": "Sequence",
+ "value": "={{ $json.Sequence }}",
+ "type": "number"
+ }
+ ]
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.set",
+ "typeVersion": 3.4,
+ "position": [
+ 1264,
+ -192
+ ],
+ "id": "c3e642c3-096f-4e21-8a3b-e49ee9fed022",
+ "name": "Edit Fields"
+ },
+ {
+ "parameters": {
+ "toRecipients": "zwitserland@dewittransport.nl",
+ "subject": "={{$json.subject}}",
+ "bodyContent": "={{$json.html}}",
+ "additionalFields": {
+ "bodyContentType": "html"
+ }
+ },
+ "type": "n8n-nodes-base.microsoftOutlook",
+ "typeVersion": 2,
+ "position": [
+ 1712,
+ -192
+ ],
+ "id": "c505a2a1-545b-401c-ad2f-7c9d0b6546c8",
+ "name": "Send a message",
+ "webhookId": "d45b20c1-ceb8-46c5-ac1e-9e94e0d77396",
+ "credentials": {
+ "microsoftOutlookOAuth2Api": {
+ "id": "PGANiL6y4sGEEvao",
+ "name": "Microsoft Outlook Palletways"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "jsCode": "// n8n Code node (Run Once for All Items)\n// Output: 1 item met { subject, html }\n\nfunction esc(v) {\n return String(v ?? '')\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction formatDateShort(dstr) {\n const s = String(dstr ?? '').substring(0, 10); // YYYY-MM-DD\n const parts = s.split('-');\n if (parts.length === 3) return `${parts[2]}-${parts[1]}-${parts[0]}`; // DD-MM-YYYY\n return s;\n}\n\nconst rows = $input.all().map(i => i.json);\n\nif (!rows.length) {\n return [{\n json: {\n subject: 'Laadoverzicht',\n html: `\n \n
\n Geen gegevens.\n \n `\n }\n }];\n}\n\n// --- Group by LoadingTripID ---\nconst tripMap = new Map();\n\nfor (const r of rows) {\n const tripId = r.LoadingTripID ?? 'Onbekend';\n const tripKey = String(tripId);\n\n if (!tripMap.has(tripKey)) {\n tripMap.set(tripKey, {\n tripId,\n truck: r.LoadingTruck ?? '',\n loadingDate: r.LoadingDate ?? '',\n lines: []\n });\n }\n\n const trip = tripMap.get(tripKey);\n\n trip.lines.push({\n sequence: Number(r.Sequence ?? 999999),\n cargonr: r.Cargonr ?? '',\n loadingPlace: String(r.LoadingPlace ?? '').trim(),\n unloadingPlace: String(r.UnloadingPlace ?? '').trim(),\n unitAmount: Number(r.Cargo_UnitAmount ?? 0) || 0,\n unitDescription: String(r.Cargo_UnitDescription ?? '').trim() || 'Onbekend'\n });\n}\n\n// sort trips numeriek als het kan, anders op string\nconst trips = Array.from(tripMap.values()).sort((a, b) => {\n const an = Number(a.tripId);\n const bn = Number(b.tripId);\n if (!Number.isNaN(an) && !Number.isNaN(bn)) return an - bn;\n return String(a.tripId).localeCompare(String(b.tripId));\n});\n\n// sort regels binnen trip op Sequence\nfor (const t of trips) {\n t.lines.sort((a, b) => {\n if (a.sequence !== b.sequence) return a.sequence - b.sequence;\n\n const ac = Number(a.cargonr);\n const bc = Number(b.cargonr);\n if (!Number.isNaN(ac) && !Number.isNaN(bc)) return ac - bc;\n\n return String(a.cargonr).localeCompare(String(b.cargonr));\n });\n}\n\nconst overallDate = formatDateShort(rows[0].LoadingDate);\n\n// --- Build HTML ---\nlet html = '';\n\nhtml += `\n\n \n \n`;\n\nhtml += `\n
\n
LoadingDate
\n
${esc(overallDate)}
\n
\n`;\n\nfor (const t of trips) {\n html += `\n
\n
\n LoadingTripID | LoadingTruck\n
\n
\n ${esc(t.tripId)} \n ${esc(t.truck)}\n
\n
\n `;\n\n html += `\n
\n \n \n \n \n \n \n | LoadingPlace | \n Amount / DeliveryPlace | \n
\n \n \n `;\n\n for (const line of t.lines) {\n const cargoText = `${esc(line.unitAmount)} ${esc(line.unitDescription)}${line.unloadingPlace ? ` (${esc(line.unloadingPlace)})` : ''}`;\n\n html += `\n \n | \n ${esc(line.loadingPlace)}\n | \n \n ${cargoText}\n | \n
\n `;\n }\n\n html += `\n \n
\n `;\n}\n\nhtml += `\n
\n Dit overzicht is automatisch gegenereerd.\n
\n
\n \n\n`;\n\nconst subject = `Laadoverzicht ${overallDate}`;\n\nreturn [{\n json: { subject, html }\n}];"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ 1488,
+ -192
+ ],
+ "id": "0daa4276-1ac6-412d-bdec-467f5cc5209c",
+ "name": "Code in JavaScript"
+ },
+ {
+ "parameters": {
+ "protocol": "sftp",
+ "operation": "list",
+ "path": "\\dewit\\lommerse",
+ "options": {}
+ },
+ "id": "1583d688-1128-4f0c-a2c4-2d4865a2f6fe",
+ "name": "List XML Files",
+ "type": "n8n-nodes-base.ftp",
+ "typeVersion": 1,
+ "position": [
+ 144,
+ -192
+ ],
+ "credentials": {
+ "sftp": {
+ "id": "nuxGYvlfDSPCAONf",
+ "name": "n8n 2"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "protocol": "sftp",
+ "path": "={{ $json.path }}",
+ "options": {}
+ },
+ "id": "fca75cef-d197-4341-90d5-68e28b2790e4",
+ "name": "Download XML File",
+ "type": "n8n-nodes-base.ftp",
+ "typeVersion": 1,
+ "position": [
+ 368,
+ -192
+ ],
+ "credentials": {
+ "sftp": {
+ "id": "nuxGYvlfDSPCAONf",
+ "name": "n8n 2"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "options": {}
+ },
+ "type": "n8n-nodes-base.xml",
+ "typeVersion": 1,
+ "position": [
+ 816,
+ -192
+ ],
+ "id": "f6cbc7bc-152c-4597-bb03-1cf16357f4d5",
+ "name": "XML"
+ },
+ {
+ "parameters": {
+ "jsCode": "// Split $json.shipments.shipments -> losse items\n// Werkt ook als er meerdere input items binnenkomen\n\nconst out = [];\n\nfor (const row of $input.all()) {\n const list = row.json?.shipments?.shipments;\n\n if (!list) continue;\n\n const arr = Array.isArray(list) ? list : [list];\n\n for (const s of arr) {\n out.push({ json: s });\n }\n}\n\nout.sort((a, b) => {\n const ta = Number(a.json.LoadingTripID ?? 0);\n const tb = Number(b.json.LoadingTripID ?? 0);\n if (ta !== tb) return ta - tb;\n\n const sa = Number(a.json.Sequence ?? 0);\n const sb = Number(b.json.Sequence ?? 0);\n return sa - sb;\n});\n\nreturn out;\n\n"
+ },
+ "id": "219af45b-77f2-407c-963f-7eb1409d5754",
+ "name": "Splits items",
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ 1040,
+ -192
+ ]
+ },
+ {
+ "parameters": {
+ "protocol": "sftp",
+ "operation": "delete",
+ "path": "={{ $('List XML Files').item.json.path }}",
+ "options": {}
+ },
+ "type": "n8n-nodes-base.ftp",
+ "typeVersion": 1,
+ "position": [
+ 832,
+ 0
+ ],
+ "id": "9b8604f1-b6e8-4079-ac50-38d36cd07ce9",
+ "name": "FTP",
+ "credentials": {
+ "sftp": {
+ "id": "nuxGYvlfDSPCAONf",
+ "name": "n8n 2"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "rule": {
+ "interval": [
+ {
+ "field": "minutes"
+ }
+ ]
+ }
+ },
+ "type": "n8n-nodes-base.scheduleTrigger",
+ "typeVersion": 1.3,
+ "position": [
+ -48,
+ -192
+ ],
+ "id": "f3e50052-287a-460c-854e-e3b47333579b",
+ "name": "Schedule Trigger"
+ },
+ {
+ "parameters": {
+ "jsCode": "// n8n Code node (Run Once for All Items)\n// Output: 1 item met { subject, html }\n\nfunction esc(v) {\n return String(v ?? '')\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction formatDateShort(dstr) {\n const s = String(dstr ?? '').substring(0, 10); // YYYY-MM-DD\n const parts = s.split('-');\n if (parts.length === 3) return `${parts[2]}-${parts[1]}-${parts[0]}`; // DD-MM-YYYY\n return s;\n}\n\nconst rows = $input.all().map(i => i.json);\n\nif (!rows.length) {\n return [{\n json: {\n subject: 'Laadoverzicht',\n html: `\n \n \n Geen gegevens.\n \n `\n }\n }];\n}\n\n// --- Group by LoadingTripID (in insertion order) ---\nconst tripMap = new Map();\n\nfor (const r of rows) {\n const tripId = r.LoadingTripID ?? 'Onbekend';\n const tripKey = String(tripId);\n\n if (!tripMap.has(tripKey)) {\n tripMap.set(tripKey, {\n tripId,\n truck: r.LoadingTruck ?? '',\n loadingDate: r.LoadingDate ?? '',\n cargos: new Map(),\n order: []\n });\n }\n\n const trip = tripMap.get(tripKey);\n\n const cargonr = r.Cargonr ?? 'Onbekend';\n let cargoKey = String(cargonr);\n\n let cg = trip.cargos.get(cargoKey);\n if (cg && (cg.place ?? '') !== (r.LoadingPlace ?? '')) {\n cargoKey = `${cargoKey}||${r.LoadingPlace ?? ''}`;\n cg = trip.cargos.get(cargoKey);\n }\n\n if (!cg) {\n cg = {\n cargonr,\n place: r.LoadingPlace ?? '',\n date: r.LoadingDate ?? trip.loadingDate ?? '',\n units: new Map()\n };\n trip.cargos.set(cargoKey, cg);\n trip.order.push(cargoKey);\n }\n\n const desc = String(r.Cargo_UnitDescription ?? '').trim() || 'Onbekend';\n const amt = Number(r.Cargo_UnitAmount ?? 0) || 0;\n cg.units.set(desc, (cg.units.get(desc) || 0) + amt);\n}\n\n// sort trips numeriek als het kan, anders op string\nconst trips = Array.from(tripMap.values()).sort((a, b) => {\n const an = Number(a.tripId), bn = Number(b.tripId);\n if (!Number.isNaN(an) && !Number.isNaN(bn)) return an - bn;\n return String(a.tripId).localeCompare(String(b.tripId));\n});\n\nconst overallDate = formatDateShort(rows[0].LoadingDate);\n\n// --- Build HTML (base font-size via CSS var; rest via em) ---\nlet html = '';\nhtml += `\n\n \n`;\n\nhtml += `\n \n
LoadingDate
\n
${esc(overallDate)}
\n
\n`;\n\nfor (const t of trips) {\n const tripDate = formatDateShort(t.loadingDate);\n\n html += `\n \n
\n LoadingTripID | LoadingTruck\n
\n\n
\n ${esc(t.tripId)} \n ${esc(t.truck)}\n
\n\n ${tripDate ? `
LoadingDate: ${esc(tripDate)}
` : ''}\n
\n `;\n\n html += `\n \n \n \n | LoadingPlace | \n Cargo_UnitAmount / Cargo_UnitDescription | \n
\n \n \n `;\n\n for (const key of t.order) {\n const cg = t.cargos.get(key);\n\n const placeCell = `${esc(cg.place)}
`;\n\n const unitLines = Array.from(cg.units.entries())\n .sort((a, b) => String(a[0]).localeCompare(String(b[0])))\n .map(([desc, amt]) => `${esc(amt)} ${esc(desc)}`)\n .join('
');\n\n html += `\n \n | ${placeCell} | \n ${unitLines || ' '} | \n
\n `;\n }\n\n html += `\n \n
\n `;\n}\n\nhtml += `\n \n Dit overzicht is automatisch gegenereerd.\n
\n`;\n\nhtml += `\n \n\n`;\n\nconst subject = `Laadoverzicht ${overallDate}`;\n\nreturn [{\n json: { subject, html }\n}];"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ 1472,
+ -352
+ ],
+ "id": "ac4ee625-1439-4bc0-aa2f-8c109098cd6f",
+ "name": "Code in JavaScript1"
+ }
+ ],
+ "pinData": {},
+ "connections": {
+ "Extract from File": {
+ "main": [
+ [
+ {
+ "node": "XML",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "FTP",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Edit Fields": {
+ "main": [
+ [
+ {
+ "node": "Code in JavaScript",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Code in JavaScript": {
+ "main": [
+ [
+ {
+ "node": "Send a message",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "List XML Files": {
+ "main": [
+ [
+ {
+ "node": "Download XML File",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Download XML File": {
+ "main": [
+ [
+ {
+ "node": "Extract from File",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "XML": {
+ "main": [
+ [
+ {
+ "node": "Splits items",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Splits items": {
+ "main": [
+ [
+ {
+ "node": "Edit Fields",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Schedule Trigger": {
+ "main": [
+ [
+ {
+ "node": "List XML Files",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ }
+ },
+ "active": true,
+ "settings": {
+ "executionOrder": "v1",
+ "binaryMode": "separate",
+ "availableInMCP": false
+ },
+ "versionId": "7bb65e02-3a9a-45c4-824a-bd5805f6d2d3",
+ "meta": {
+ "templateCredsSetupCompleted": true,
+ "instanceId": "bef8d409866a58c0777dfe7cca1b9c2400fd051c056d361501393ab423006b5f"
+ },
+ "nodeGroups": [],
+ "id": "7WqVSI1DxMlMrIw-ArAaM",
+ "tags": []
+}
\ No newline at end of file