diff --git a/Gheeraert/Gheeraert Workflow 2 - OrderID ophalen.json b/Gheeraert/Gheeraert Workflow 2 - OrderID ophalen.json
new file mode 100644
index 0000000..da76f84
--- /dev/null
+++ b/Gheeraert/Gheeraert Workflow 2 - OrderID ophalen.json
@@ -0,0 +1,357 @@
+{
+ "name": "Gheeraert Workflow 2 - OrderID ophalen",
+ "nodes": [
+ {
+ "parameters": {
+ "method": "POST",
+ "url": "https://my-uat.gheeraert.be/token",
+ "sendBody": true,
+ "contentType": "form-urlencoded",
+ "bodyParameters": {
+ "parameters": [
+ {
+ "name": "grant_type",
+ "value": "password"
+ },
+ {
+ "name": "username",
+ "value": "api@dewittransport.nl"
+ },
+ {
+ "name": "password",
+ "value": "my.gheeraert"
+ }
+ ]
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.httpRequest",
+ "typeVersion": 4.4,
+ "position": [
+ -368,
+ -96
+ ],
+ "id": "f5b22505-d130-43e6-9600-85e55051d7cb",
+ "name": "Get Gheeraert Token"
+ },
+ {
+ "parameters": {
+ "operation": "executeQuery",
+ "query": "SELECT\n id,\n purchase_order_number,\n transpas_legnr,\n transpas_reference,\n file_name\nFROM public.ghe_order_log\nWHERE file_upload_status = 'sent'\n AND gheeraert_order_id IS NULL\n AND is_closed = false\n AND (\n next_status_poll_at IS NULL\n OR next_status_poll_at <= CURRENT_TIMESTAMP\n )\nORDER BY\n COALESCE(last_order_lookup_at, created_at),\n id\nLIMIT 25;",
+ "options": {}
+ },
+ "type": "n8n-nodes-base.postgres",
+ "typeVersion": 2.6,
+ "position": [
+ -144,
+ -96
+ ],
+ "id": "1c71fb93-6d01-4187-aa56-b5816f37ab6b",
+ "name": "Get Orders Without Gheeraert ID",
+ "credentials": {
+ "postgres": {
+ "id": "pxziuCOuNpsUFKrm",
+ "name": "transpas_n8n_gheeraert"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "jsCode": "return $input.all().map(item => {\n const purchaseOrderNumber = String(item.json.purchase_order_number || '').trim();\n\n return {\n json: {\n ...item.json,\n encodedPurchaseOrderNumber: encodeURIComponent(purchaseOrderNumber),\n },\n };\n});"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ 80,
+ -96
+ ],
+ "id": "5fb9dc99-37cb-4a46-bde7-e43bc94bbf34",
+ "name": "Prepare PurchaseOrder Lookup"
+ },
+ {
+ "parameters": {
+ "jsCode": "return $input.all().map(item => {\n const j = item.json;\n\n let response = j.gheeraertOrder;\n\n // In jouw output is gheeraertOrder een JSON-string.\n if (typeof response === 'string') {\n try {\n response = JSON.parse(response);\n } catch (e) {\n response = null;\n }\n }\n\n // Fallback als response direct op json staat.\n if (!response && (j.OrderID || j.ID || j.orderID)) {\n response = j;\n }\n\n // Soms komt API als array terug.\n const order = Array.isArray(response) ? response[0] : response;\n\n const rawOrderId =\n order?.OrderID ??\n order?.orderID ??\n order?.ID ??\n order?.id ??\n null;\n\n const gheeraertOrderId = rawOrderId !== null ? Number(rawOrderId) : null;\n const found = Number.isFinite(gheeraertOrderId) && gheeraertOrderId > 0;\n\n let errorMessage = '';\n\n if (j.error?.message) {\n errorMessage = j.error.message;\n }\n\n let apiMessage = '';\n\n if (errorMessage.includes('Order not found')) {\n apiMessage = 'Gheeraert OrderID nog niet gevonden';\n } else if (errorMessage) {\n apiMessage = errorMessage.slice(0, 500);\n }\n\n return {\n json: {\n ...j,\n\n found,\n gheeraertOrderId: found ? Math.trunc(gheeraertOrderId) : null,\n\n gheeraertTrackingId: order?.TrackingID ?? null,\n gheeraertPurchaseOrderNumber: order?.PurchaseOrderNumber ?? null,\n gheeraertExtOrderNumber: order?.ExtOrderNumber ?? null,\n gheeraertExtDelNoteNumber: order?.ExtDelNoteNumber ?? null,\n\n lookupMessage: found\n ? `Gheeraert OrderID gevonden: ${Math.trunc(gheeraertOrderId)}`\n : (apiMessage || 'Gheeraert OrderID nog niet gevonden'),\n },\n };\n});"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ 752,
+ -96
+ ],
+ "id": "5011b72f-6521-49a6-8d6b-30447bd9e2b5",
+ "name": "Normalize Gheeraert Lookup"
+ },
+ {
+ "parameters": {
+ "conditions": {
+ "options": {
+ "caseSensitive": true,
+ "leftValue": "",
+ "typeValidation": "strict",
+ "version": 3
+ },
+ "conditions": [
+ {
+ "id": "7da7e84c-8efd-459f-987a-7b3676b19c06",
+ "leftValue": "={{ $json.found }}",
+ "rightValue": "true",
+ "operator": {
+ "type": "boolean",
+ "operation": "true",
+ "singleValue": true
+ }
+ }
+ ],
+ "combinator": "and"
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.if",
+ "typeVersion": 2.3,
+ "position": [
+ 976,
+ -96
+ ],
+ "id": "168fc7f2-8447-4b76-a8cf-24c5be91604e",
+ "name": "OrderID Found?"
+ },
+ {
+ "parameters": {
+ "operation": "executeQuery",
+ "query": "UPDATE public.ghe_order_log\nSET\n gheeraert_order_id = $1::integer,\n last_order_lookup_at = CURRENT_TIMESTAMP,\n next_status_poll_at = CURRENT_TIMESTAMP,\n file_upload_error = NULL\nWHERE id = $2::bigint;",
+ "options": {
+ "queryReplacement": "=$1 = {{ $json.gheeraertOrderId }}\n$2 = {{ $json.id }}"
+ }
+ },
+ "type": "n8n-nodes-base.postgres",
+ "typeVersion": 2.6,
+ "position": [
+ 1200,
+ -192
+ ],
+ "id": "3503f64e-1483-4401-825e-22cac20fab82",
+ "name": "Update Gheeraert OrderID",
+ "credentials": {
+ "postgres": {
+ "id": "pxziuCOuNpsUFKrm",
+ "name": "transpas_n8n_gheeraert"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "operation": "executeQuery",
+ "query": "UPDATE public.ghe_order_log\nSET\n last_order_lookup_at = CURRENT_TIMESTAMP,\n next_status_poll_at = CURRENT_TIMESTAMP + INTERVAL '10 minutes',\n file_upload_error = $1::text\nWHERE id = $2::bigint\n AND gheeraert_order_id IS NULL;",
+ "options": {
+ "queryReplacement": "=$1 = {{ $json.lookupMessage }}\n$2 = {{ $json.id }}"
+ }
+ },
+ "type": "n8n-nodes-base.postgres",
+ "typeVersion": 2.6,
+ "position": [
+ 1200,
+ 0
+ ],
+ "id": "994d3fcc-4926-40de-a8fa-fe64f5aabbee",
+ "name": "OrderID Not Found Retry Later",
+ "credentials": {
+ "postgres": {
+ "id": "pxziuCOuNpsUFKrm",
+ "name": "transpas_n8n_gheeraert"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "url": "=https://my-uat.gheeraert.be/api/Order/ByPurchaseOrderNumber/{{$json.encodedPurchaseOrderNumber}}",
+ "sendHeaders": true,
+ "headerParameters": {
+ "parameters": [
+ {
+ "name": "Authorization",
+ "value": "=Bearer {{ $('Get Gheeraert Token').first().json.access_token }}"
+ },
+ {
+ "name": "Accept",
+ "value": "application/json"
+ }
+ ]
+ },
+ "options": {
+ "response": {
+ "response": {
+ "responseFormat": "text",
+ "outputPropertyName": "gheeraertOrder"
+ }
+ }
+ }
+ },
+ "type": "n8n-nodes-base.httpRequest",
+ "typeVersion": 4.4,
+ "position": [
+ 304,
+ -32
+ ],
+ "id": "f8874799-dbed-4a32-86ee-ae56fcafebf3",
+ "name": "Lookup Gheeraert OrderID",
+ "notesInFlow": false,
+ "onError": "continueRegularOutput"
+ },
+ {
+ "parameters": {
+ "mode": "combine",
+ "combineBy": "combineByPosition",
+ "options": {}
+ },
+ "type": "n8n-nodes-base.merge",
+ "typeVersion": 3.2,
+ "position": [
+ 528,
+ -96
+ ],
+ "id": "a4c3324a-8ad3-40dd-8267-33c91af7740a",
+ "name": "Merge"
+ },
+ {
+ "parameters": {
+ "rule": {
+ "interval": [
+ {
+ "field": "cronExpression",
+ "expression": "*/20 6-22 * * 1-5"
+ }
+ ]
+ }
+ },
+ "type": "n8n-nodes-base.scheduleTrigger",
+ "typeVersion": 1.3,
+ "position": [
+ -592,
+ -96
+ ],
+ "id": "61885d7b-a67f-477a-a772-5063858bc108",
+ "name": "Schedule Trigger"
+ }
+ ],
+ "pinData": {},
+ "connections": {
+ "Get Gheeraert Token": {
+ "main": [
+ [
+ {
+ "node": "Get Orders Without Gheeraert ID",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Get Orders Without Gheeraert ID": {
+ "main": [
+ [
+ {
+ "node": "Prepare PurchaseOrder Lookup",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Prepare PurchaseOrder Lookup": {
+ "main": [
+ [
+ {
+ "node": "Merge",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "Lookup Gheeraert OrderID",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Normalize Gheeraert Lookup": {
+ "main": [
+ [
+ {
+ "node": "OrderID Found?",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "OrderID Found?": {
+ "main": [
+ [
+ {
+ "node": "Update Gheeraert OrderID",
+ "type": "main",
+ "index": 0
+ }
+ ],
+ [
+ {
+ "node": "OrderID Not Found Retry Later",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Update Gheeraert OrderID": {
+ "main": [
+ []
+ ]
+ },
+ "Lookup Gheeraert OrderID": {
+ "main": [
+ [
+ {
+ "node": "Merge",
+ "type": "main",
+ "index": 1
+ }
+ ]
+ ]
+ },
+ "Merge": {
+ "main": [
+ [
+ {
+ "node": "Normalize Gheeraert Lookup",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Schedule Trigger": {
+ "main": [
+ [
+ {
+ "node": "Get Gheeraert Token",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ }
+ },
+ "active": false,
+ "settings": {
+ "executionOrder": "v1",
+ "binaryMode": "separate"
+ },
+ "versionId": "1d9a70a4-7abe-4927-a8ca-ababafe107e6",
+ "meta": {
+ "templateCredsSetupCompleted": true,
+ "instanceId": "bef8d409866a58c0777dfe7cca1b9c2400fd051c056d361501393ab423006b5f"
+ },
+ "nodeGroups": [],
+ "id": "BHfu3wTZCa8C9Gwr",
+ "tags": []
+}
\ No newline at end of file
diff --git a/Gheeraert/Gheeraert workflow 3 — statussen ophalen.json b/Gheeraert/Gheeraert workflow 3 — statussen ophalen.json
new file mode 100644
index 0000000..663ba69
--- /dev/null
+++ b/Gheeraert/Gheeraert workflow 3 — statussen ophalen.json
@@ -0,0 +1,337 @@
+{
+ "name": "Gheeraert workflow 3 — statussen ophalen",
+ "nodes": [
+ {
+ "parameters": {
+ "operation": "executeQuery",
+ "query": "SELECT\n id,\n purchase_order_number,\n transpas_legnr,\n transpas_reference,\n gheeraert_order_id,\n last_status_poll_at,\n next_status_poll_at,\n delivered_at,\n pod_online_at,\n pod_downloaded_at\nFROM public.ghe_order_log\nWHERE file_upload_status = 'sent'\n AND gheeraert_order_id IS NOT NULL\n AND is_closed = false\n AND (\n next_status_poll_at IS NULL\n OR next_status_poll_at <= CURRENT_TIMESTAMP\n )\nORDER BY\n COALESCE(last_status_poll_at, created_at),\n id\nLIMIT 25;",
+ "options": {}
+ },
+ "type": "n8n-nodes-base.postgres",
+ "typeVersion": 2.6,
+ "position": [
+ 224,
+ -272
+ ],
+ "id": "efb74e39-f147-40e3-af5b-c8022ad51944",
+ "name": "Get Orders With Gheeraert ID",
+ "credentials": {
+ "postgres": {
+ "id": "pxziuCOuNpsUFKrm",
+ "name": "transpas_n8n_gheeraert"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "method": "POST",
+ "url": "https://my-uat.gheeraert.be/token",
+ "sendBody": true,
+ "contentType": "form-urlencoded",
+ "bodyParameters": {
+ "parameters": [
+ {
+ "name": "grant_type",
+ "value": "password"
+ },
+ {
+ "name": "username",
+ "value": "api@dewittransport.nl"
+ },
+ {
+ "name": "password",
+ "value": "my.gheeraert"
+ }
+ ]
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.httpRequest",
+ "typeVersion": 4.4,
+ "position": [
+ 448,
+ -272
+ ],
+ "id": "b049495e-5e3d-4c86-8ad7-5283fdf82efe",
+ "name": "Get Gheeraert Token"
+ },
+ {
+ "parameters": {
+ "url": "={{ 'https://my-uat.gheeraert.be/api/OrderTracking/' + $('Get Orders With Gheeraert ID').item.json.gheeraert_order_id }}",
+ "sendHeaders": true,
+ "headerParameters": {
+ "parameters": [
+ {
+ "name": "Authorization",
+ "value": "={{ 'Bearer ' + $('Get Gheeraert Token').first().json.access_token }}"
+ },
+ {
+ "name": "Accept",
+ "value": "application/json"
+ }
+ ]
+ },
+ "options": {
+ "response": {
+ "response": {
+ "responseFormat": "json"
+ }
+ }
+ }
+ },
+ "type": "n8n-nodes-base.httpRequest",
+ "typeVersion": 4.4,
+ "position": [
+ 672,
+ -272
+ ],
+ "id": "1407cf2f-34b0-4a58-bdc3-86ac1ea72777",
+ "name": "Lookup Gheeraert OrderTracking",
+ "onError": "continueRegularOutput"
+ },
+ {
+ "parameters": {
+ "jsCode": "function simpleHash(input) {\n const str = String(input ?? '');\n let h1 = 0xdeadbeef ^ str.length;\n let h2 = 0x41c6ce57 ^ str.length;\n\n for (let i = 0; i < str.length; i++) {\n const ch = str.charCodeAt(i);\n h1 = Math.imul(h1 ^ ch, 2654435761);\n h2 = Math.imul(h2 ^ ch, 1597334677);\n }\n\n h1 =\n Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^\n Math.imul(h2 ^ (h2 >>> 13), 3266489909);\n\n h2 =\n Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^\n Math.imul(h1 ^ (h1 >>> 13), 3266489909);\n\n return (\n (h2 >>> 0).toString(16).padStart(8, '0') +\n (h1 >>> 0).toString(16).padStart(8, '0')\n );\n}\n\nreturn $input.all().map(item => {\n const j = item.json;\n\n const base = [\n j.gheeraert_order_id ?? '',\n j.status_id ?? '',\n j.tracking_date ?? '',\n j.tracking_comments ?? '',\n j.barcode ?? '',\n j.tracking_url ?? '',\n ].join('|');\n\n return {\n json: {\n ...j,\n status_hash: simpleHash(base),\n },\n };\n});"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ 1120,
+ -272
+ ],
+ "id": "c517653a-9b40-4c9a-a1a8-c7e0f0cf3636",
+ "name": "Make Status Hash"
+ },
+ {
+ "parameters": {
+ "operation": "executeQuery",
+ "query": "WITH input AS (\n SELECT $1::jsonb AS j\n),\ntyped AS (\n SELECT\n (j->>'order_log_id')::bigint AS order_log_id,\n (j->>'gheeraert_order_id')::integer AS gheeraert_order_id,\n (j->>'purchase_order_number')::varchar AS purchase_order_number,\n (j->>'status_id')::integer AS status_id,\n (j->>'tracking_date')::timestamp AS tracking_date,\n (j->>'tracking_comments')::text AS tracking_comments,\n (j->>'tracking_url')::text AS tracking_url,\n NULLIF(j->>'barcode', '')::varchar AS barcode,\n NULLIF(j->>'user_name', '')::varchar AS user_name,\n (j->>'status_hash')::varchar AS status_hash\n FROM input\n),\nins AS (\n INSERT INTO public.ghe_status_log (\n order_log_id,\n gheeraert_order_id,\n purchase_order_number,\n status_id,\n tracking_date,\n tracking_comments,\n tracking_url,\n barcode,\n user_name,\n status_hash\n )\n SELECT\n order_log_id,\n gheeraert_order_id,\n purchase_order_number,\n status_id,\n tracking_date,\n tracking_comments,\n tracking_url,\n barcode,\n user_name,\n status_hash\n FROM typed\n ON CONFLICT (status_hash)\n DO NOTHING\n RETURNING id\n)\nSELECT\n ins.id AS inserted_id,\n typed.order_log_id,\n typed.gheeraert_order_id,\n typed.purchase_order_number,\n typed.status_id,\n typed.tracking_date,\n typed.tracking_comments,\n typed.tracking_url,\n typed.barcode,\n typed.user_name,\n typed.status_hash\nFROM typed\nLEFT JOIN ins ON TRUE;",
+ "options": {
+ "queryReplacement": "={{ [JSON.stringify($json)] }}"
+ }
+ },
+ "type": "n8n-nodes-base.postgres",
+ "typeVersion": 2.6,
+ "position": [
+ 1440,
+ -272
+ ],
+ "id": "0612800b-26e7-415d-8bf3-225edbbb93e4",
+ "name": "Insert ghe_status_log",
+ "credentials": {
+ "postgres": {
+ "id": "pxziuCOuNpsUFKrm",
+ "name": "transpas_n8n_gheeraert"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "conditions": {
+ "options": {
+ "caseSensitive": true,
+ "leftValue": "",
+ "typeValidation": "strict",
+ "version": 3
+ },
+ "conditions": [
+ {
+ "id": "5d26cbeb-2ed9-45a4-914b-754a130fc915",
+ "leftValue": "={{ $json.inserted_id }}",
+ "rightValue": "",
+ "operator": {
+ "type": "string",
+ "operation": "notEmpty",
+ "singleValue": true
+ }
+ }
+ ],
+ "combinator": "and"
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.if",
+ "typeVersion": 2.3,
+ "position": [
+ 1664,
+ -272
+ ],
+ "id": "7ef41b79-2a94-4ab3-9e0d-aecf274dcd7e",
+ "name": "If"
+ },
+ {
+ "parameters": {
+ "operation": "executeQuery",
+ "query": "\nWITH input AS (\n SELECT $1::jsonb AS j\n)\nUPDATE public.ghe_order_log\nSET\n last_status_poll_at = CURRENT_TIMESTAMP,\n last_status_id = (j->>'status_id')::integer,\n last_status_text = (j->>'tracking_comments')::varchar,\n last_tracking_date = (j->>'tracking_date')::timestamp,\n\n delivered_at = CASE\n WHEN (j->>'status_id')::integer = 30 AND delivered_at IS NULL\n THEN (j->>'tracking_date')::timestamp\n ELSE delivered_at\n END,\n\n pod_online_at = CASE\n WHEN (j->>'status_id')::integer = 35 AND pod_online_at IS NULL\n THEN (j->>'tracking_date')::timestamp\n ELSE pod_online_at\n END,\n\n next_status_poll_at = CASE\n WHEN (j->>'status_id')::integer = 35 THEN CURRENT_TIMESTAMP + INTERVAL '1 day'\n WHEN (j->>'status_id')::integer = 30 THEN CURRENT_TIMESTAMP + INTERVAL '2 hours'\n ELSE CURRENT_TIMESTAMP + INTERVAL '15 minutes'\n END\nFROM input\nWHERE public.ghe_order_log.id = (j->>'order_log_id')::bigint;",
+ "options": {
+ "queryReplacement": "=={{ [JSON.stringify($json)] }}"
+ }
+ },
+ "type": "n8n-nodes-base.postgres",
+ "typeVersion": 2.6,
+ "position": [
+ 1920,
+ -336
+ ],
+ "id": "bb2fd5f0-0958-41aa-b739-a93689508021",
+ "name": "Update ghe_order_log",
+ "credentials": {
+ "postgres": {
+ "id": "pxziuCOuNpsUFKrm",
+ "name": "transpas_n8n_gheeraert"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "content": "## workflow 1: orders versturen + loggen\n## workflow 2: Gheeraert OrderID ophalen\n## workflow 3: tracking ophalen + statuslog vullen\n",
+ "width": 720
+ },
+ "type": "n8n-nodes-base.stickyNote",
+ "typeVersion": 1,
+ "position": [
+ 48,
+ -464
+ ],
+ "id": "3d0643a3-a9ab-481f-b8e3-cd00c9d54628",
+ "name": "Sticky Note"
+ },
+ {
+ "parameters": {
+ "rule": {
+ "interval": [
+ {
+ "field": "cronExpression",
+ "expression": "*/20 6-22 * * 1-5"
+ }
+ ]
+ }
+ },
+ "type": "n8n-nodes-base.scheduleTrigger",
+ "typeVersion": 1.3,
+ "position": [
+ 0,
+ -272
+ ],
+ "id": "201f3623-82ac-43a6-aade-8d4d967a43bf",
+ "name": "Schedule Trigger"
+ },
+ {
+ "parameters": {
+ "jsCode": "const orders = $('Get Orders With Gheeraert ID').all().map(i => i.json);\n\nconst orderByGheeraertId = new Map(\n orders.map(order => [String(order.gheeraert_order_id), order])\n);\n\nconst out = [];\n\nfor (const item of $input.all()) {\n const t = item.json;\n\n const gheOrderId = String(t.OrderID ?? '');\n const order = orderByGheeraertId.get(gheOrderId);\n\n if (!order) {\n out.push({\n json: {\n error: true,\n error_text: `Geen matching order gevonden voor Gheeraert OrderID ${gheOrderId}`,\n gheeraert_order_id: t.OrderID ?? null,\n purchase_order_number: t.PurchaseOrderNumber ?? null,\n raw: t,\n },\n });\n continue;\n }\n\n out.push({\n json: {\n order_log_id: order.id ?? null,\n gheeraert_order_id: t.OrderID ?? order.gheeraert_order_id ?? null,\n purchase_order_number: t.PurchaseOrderNumber ?? order.purchase_order_number ?? null,\n transpas_legnr: order.transpas_legnr ?? t.ExtOrderNumber ?? null,\n transpas_reference: order.transpas_reference ?? null,\n\n status_id: t.StatusID ?? null,\n tracking_date: t.TrackingDate ?? null,\n tracking_comments: t.PublicTrackingComments ?? null,\n tracking_url: t.TrackingURL ?? null,\n barcode: t.Barcode ?? null,\n user_name: t.UserName ?? null,\n\n gheeraert_track_id: t.TrackID ?? null,\n location: t.Location ?? null,\n eta: t.ETA ?? null,\n raw: t,\n },\n });\n}\n\nreturn out;"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ 896,
+ -272
+ ],
+ "id": "0e004fcc-f058-4f5c-aa75-f4ee40768295",
+ "name": "Attach Order Context"
+ }
+ ],
+ "pinData": {},
+ "connections": {
+ "Get Orders With Gheeraert ID": {
+ "main": [
+ [
+ {
+ "node": "Get Gheeraert Token",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Get Gheeraert Token": {
+ "main": [
+ [
+ {
+ "node": "Lookup Gheeraert OrderTracking",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Lookup Gheeraert OrderTracking": {
+ "main": [
+ [
+ {
+ "node": "Attach Order Context",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Make Status Hash": {
+ "main": [
+ [
+ {
+ "node": "Insert ghe_status_log",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Insert ghe_status_log": {
+ "main": [
+ [
+ {
+ "node": "If",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "If": {
+ "main": [
+ [
+ {
+ "node": "Update ghe_order_log",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Schedule Trigger": {
+ "main": [
+ [
+ {
+ "node": "Get Orders With Gheeraert ID",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Attach Order Context": {
+ "main": [
+ [
+ {
+ "node": "Make Status Hash",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ }
+ },
+ "active": false,
+ "settings": {
+ "executionOrder": "v1",
+ "binaryMode": "separate"
+ },
+ "versionId": "0c994af9-2e64-48c1-80ca-cb00d8ae00e5",
+ "meta": {
+ "templateCredsSetupCompleted": true,
+ "instanceId": "bef8d409866a58c0777dfe7cca1b9c2400fd051c056d361501393ab423006b5f"
+ },
+ "nodeGroups": [],
+ "id": "csPbDxZ03HyBjtwh",
+ "tags": []
+}
\ No newline at end of file
diff --git a/Gheeraert/Gheeraert workflow 4 — Transpas status XML export.json b/Gheeraert/Gheeraert workflow 4 — Transpas status XML export.json
new file mode 100644
index 0000000..e92af84
--- /dev/null
+++ b/Gheeraert/Gheeraert workflow 4 — Transpas status XML export.json
@@ -0,0 +1,262 @@
+{
+ "name": "Gheeraert workflow 4 — Transpas status XML export",
+ "nodes": [
+ {
+ "parameters": {
+ "operation": "executeQuery",
+ "query": "SELECT\n id,\n order_log_id,\n gheeraert_order_id,\n purchase_order_number,\n status_id,\n tracking_date,\n tracking_comments,\n tracking_url,\n barcode,\n user_name,\n transpas_export_status\nFROM public.ghe_status_log\nWHERE COALESCE(transpas_export_status, 'pending') = 'pending'\nORDER BY tracking_date, id\nLIMIT 100;",
+ "options": {}
+ },
+ "type": "n8n-nodes-base.postgres",
+ "typeVersion": 2.6,
+ "position": [
+ -1552,
+ -192
+ ],
+ "id": "a00a8bbb-bf11-4292-9520-86aea2ae2dc1",
+ "name": "Get pending ghe_status_log",
+ "credentials": {
+ "postgres": {
+ "id": "pxziuCOuNpsUFKrm",
+ "name": "transpas_n8n_gheeraert"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "jsCode": "// n8n Code node: Gheeraert status -> Transpas status XML\n// Mode: Run once for each item\n\nconst AMP_FIX_RE = /&(?!amp;|lt;|gt;|quot;|apos;|#\\d+;|#x[0-9A-Fa-f]+;)/g;\n\nfunction xmlEsc(value) {\n return String(value ?? '')\n .replace(AMP_FIX_RE, '&')\n .replace(//g, '>')\n .replace(/\\\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction formatATDateTime(input) {\n if (!input) return '';\n const d = new Date(input);\n if (isNaN(d.getTime())) return '';\n const pad = (n) => String(n).padStart(2, '0');\n return (\n d.getFullYear() +\n pad(d.getMonth() + 1) +\n pad(d.getDate()) +\n pad(d.getHours()) +\n pad(d.getMinutes()) +\n pad(d.getSeconds())\n );\n}\n\nfunction safeFilePart(value) {\n return String(value || '')\n .replace(/[^a-zA-Z0-9._-]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .slice(0, 80) || 'status';\n}\n\nfunction nowStamp() {\n const d = new Date();\n const p = (n) => String(n).padStart(2, '0');\n return `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}_${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}_${String(d.getMilliseconds()).padStart(3, '0')}`;\n}\n\nfunction mapStatus(statusId) {\n const sid = Number(statusId);\n switch (sid) {\n case 0: return { export: true, transpasStatus: '0', label: 'Cancelled' };\n case 5: return { export: false, transpasStatus: null, label: 'New' };\n case 10: return { export: true, transpasStatus: null, label: 'Confirmed' };\n case 20: return { export: false, transpasStatus: null, label: 'Accepted' };\n case 21: return { export: false, transpasStatus: null, label: 'Depot_Assigned' };\n case 22: return { export: false, transpasStatus: null, label: 'Planned' };\n case 23: return { export: true, transpasStatus: '23', label: 'Collected' };\n case 24: return { export: true, transpasStatus: '24', label: 'Arrived_At_Depot' };\n case 25: return { export: false, transpasStatus: null, label: 'Storage_At_Depot' };\n case 26: return { export: true, transpasStatus: '26', label: 'Shipped_From_Depot' };\n case 27: return { export: true, transpasStatus: '27', label: 'Incomplete' };\n case 28: return { export: true, transpasStatus: '28', label: 'Manco' };\n case 30: return { export: true, transpasStatus: '30', label: 'Delivered' };\n case 35: return { export: true, transpasStatus: '35', label: 'POD_Online' };\n case 40: return { export: false, transpasStatus: null, label: 'Invoiced' };\n case 50: return { export: false, transpasStatus: null, label: 'Closed' };\n case 100: return { export: false, transpasStatus: null, label: 'Custom_100' };\n default: return { export: false, transpasStatus: null, label: `Unknown_${sid}` };\n }\n}\n\nconst j = $json;\nconst mapped = mapStatus(j.status_id);\nconst atDateTime = formatATDateTime(j.tracking_date);\nconst consignmentId = String(j.purchase_order_number || '').trim();\n\nlet noteParts = [mapped.label, j.tracking_comments || ''];\nif (j.tracking_url) noteParts.push(`POD/URL: ${j.tracking_url}`);\nif (j.barcode) noteParts.push(`Barcode: ${j.barcode}`);\nconst note = noteParts.filter(Boolean).join(' - ');\n\nlet xml = '';\nlet fileName = null;\n\nif (mapped.export && consignmentId && atDateTime) {\n xml = [\n '',\n '',\n ` ${xmlEsc(atDateTime)}`,\n ' TLN',\n ' ',\n ` ${xmlEsc(consignmentId)}`,\n ` ${xmlEsc(mapped.transpasStatus)}`,\n ` ${xmlEsc(note)}`,\n ' ',\n '',\n ].join('\\n');\n\n fileName = `GHE_TLN_${safeFilePart(consignmentId)}_${safeFilePart(mapped.transpasStatus)}_${nowStamp()}.xml`;\n}\n\nreturn {\n json: {\n ...j,\n mapped_label: mapped.label,\n mapped_transpas_status: mapped.transpasStatus,\n should_export_to_transpas: mapped.export,\n transpas_xml: xml || null,\n transpas_xml_file: fileName,\n },\n};"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ -1328,
+ -192
+ ],
+ "id": "ec13ec89-6e87-4ea2-9ac1-a55bae2daef8",
+ "name": "Build Transpas XML"
+ },
+ {
+ "parameters": {
+ "conditions": {
+ "options": {
+ "caseSensitive": true,
+ "leftValue": "",
+ "typeValidation": "strict",
+ "version": 3
+ },
+ "conditions": [
+ {
+ "id": "2aa00fa4-f1bc-490f-ac20-45359523ea8f",
+ "leftValue": "={{ $json.should_export_to_transpas }}",
+ "rightValue": "",
+ "operator": {
+ "type": "boolean",
+ "operation": "true",
+ "singleValue": true
+ }
+ }
+ ],
+ "combinator": "and"
+ },
+ "options": {}
+ },
+ "type": "n8n-nodes-base.if",
+ "typeVersion": 2.3,
+ "position": [
+ -1104,
+ -192
+ ],
+ "id": "1c38efca-a152-4568-a73d-5e4b0710dea9",
+ "name": "Should export to Transpas?"
+ },
+ {
+ "parameters": {
+ "jsCode": "const xml = $json.transpas_xml;\nconst fileName = $json.transpas_xml_file || 'status.xml';\n\nconst binaryData = await this.helpers.prepareBinaryData(\n Buffer.from(xml, 'utf8'),\n fileName,\n 'application/xml'\n);\n\nreturn {\n json: {\n ...$json,\n },\n binary: {\n data: binaryData,\n },\n};"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ -880,
+ -288
+ ],
+ "id": "a384cc7c-6eed-4fdc-b1d2-0a3e0d0bbaa3",
+ "name": "Create XML Binary"
+ },
+ {
+ "parameters": {
+ "protocol": "sftp",
+ "operation": "upload",
+ "path": "=/prod/to_tp/tln/{{ $json.transpas_xml_file }}",
+ "options": {}
+ },
+ "type": "n8n-nodes-base.ftp",
+ "typeVersion": 1,
+ "position": [
+ -656,
+ -288
+ ],
+ "id": "b997c3af-a1ff-426a-b1c2-b442aa013a61",
+ "name": "SFTP Upload",
+ "credentials": {
+ "sftp": {
+ "id": "uKyzg5cSXQqXOuHI",
+ "name": "SFTP n8n"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "operation": "executeQuery",
+ "query": "UPDATE public.ghe_status_log\nSET\n transpas_xml_file = $1::varchar,\n transpas_xml_created_at = CURRENT_TIMESTAMP,\n transpas_export_status = 'exported',\n transpas_export_error = NULL\nWHERE id = $2::bigint;",
+ "options": {
+ "queryReplacement": "={{ [$json.transpas_xml_file, $json.id] }}"
+ }
+ },
+ "type": "n8n-nodes-base.postgres",
+ "typeVersion": 2.6,
+ "position": [
+ -432,
+ -288
+ ],
+ "id": "f0980ea4-86e1-40fe-b616-f6a294606aa1",
+ "name": "Mark exported",
+ "credentials": {
+ "postgres": {
+ "id": "pxziuCOuNpsUFKrm",
+ "name": "transpas_n8n_gheeraert"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "operation": "executeQuery",
+ "query": "WITH input AS (\n SELECT $1::jsonb AS j\n)\nUPDATE public.ghe_status_log\nSET\n transpas_export_status = 'ignored',\n transpas_export_error = (j->>'error_text')::text\nFROM input\nWHERE public.ghe_status_log.id = (j->>'id')::bigint;",
+ "options": {
+ "queryReplacement": "={{ [JSON.stringify({ id: $json.id, error_text: 'Status niet geëxporteerd volgens mapping: ' + ($json.mapped_label || 'onbekend') })] }}"
+ }
+ },
+ "type": "n8n-nodes-base.postgres",
+ "typeVersion": 2.6,
+ "position": [
+ -896,
+ -96
+ ],
+ "id": "051233d8-8852-4cde-b5ec-4c01d5c7fc80",
+ "name": "Mark ignored",
+ "credentials": {
+ "postgres": {
+ "id": "pxziuCOuNpsUFKrm",
+ "name": "transpas_n8n_gheeraert"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "rule": {
+ "interval": [
+ {
+ "field": "cronExpression",
+ "expression": "*/20 6-22 * * 1-5"
+ }
+ ]
+ }
+ },
+ "type": "n8n-nodes-base.scheduleTrigger",
+ "typeVersion": 1.3,
+ "position": [
+ -1760,
+ -192
+ ],
+ "id": "705a4240-1aa6-44f8-834d-03dc81cc0ca6",
+ "name": "Schedule Trigger"
+ }
+ ],
+ "pinData": {},
+ "connections": {
+ "Get pending ghe_status_log": {
+ "main": [
+ [
+ {
+ "node": "Build Transpas XML",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Build Transpas XML": {
+ "main": [
+ [
+ {
+ "node": "Should export to Transpas?",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Should export to Transpas?": {
+ "main": [
+ [
+ {
+ "node": "Create XML Binary",
+ "type": "main",
+ "index": 0
+ }
+ ],
+ [
+ {
+ "node": "Mark ignored",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Create XML Binary": {
+ "main": [
+ [
+ {
+ "node": "SFTP Upload",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "SFTP Upload": {
+ "main": [
+ [
+ {
+ "node": "Mark exported",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Schedule Trigger": {
+ "main": [
+ [
+ {
+ "node": "Get pending ghe_status_log",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ }
+ },
+ "active": false,
+ "settings": {
+ "executionOrder": "v1",
+ "binaryMode": "separate"
+ },
+ "versionId": "7006b721-596e-4654-96dd-c6d71a1e10e3",
+ "meta": {
+ "instanceId": "bef8d409866a58c0777dfe7cca1b9c2400fd051c056d361501393ab423006b5f"
+ },
+ "nodeGroups": [],
+ "id": "tgpdWats31Z0Idf6",
+ "tags": []
+}
\ No newline at end of file
diff --git a/Gheeraert/Gheeraert workflow 5 — POD naar Transpas XML.json b/Gheeraert/Gheeraert workflow 5 — POD naar Transpas XML.json
new file mode 100644
index 0000000..45eadd8
--- /dev/null
+++ b/Gheeraert/Gheeraert workflow 5 — POD naar Transpas XML.json
@@ -0,0 +1,366 @@
+{
+ "name": "Gheeraert workflow 5 — POD naar Transpas XML",
+ "nodes": [
+ {
+ "parameters": {
+ "operation": "executeQuery",
+ "query": "SELECT\n id,\n order_log_id,\n gheeraert_order_id,\n purchase_order_number,\n status_id,\n tracking_date,\n tracking_comments,\n tracking_url,\n barcode,\n user_name,\n pod_downloaded,\n pod_file_name,\n pod_downloaded_at,\n transpas_export_status,\n transpas_export_error\nFROM public.ghe_status_log\nWHERE status_id = 35\n AND tracking_url IS NOT NULL\n AND COALESCE(pod_downloaded, false) = false\nORDER BY tracking_date, id\nLIMIT 50;",
+ "options": {}
+ },
+ "type": "n8n-nodes-base.postgres",
+ "typeVersion": 2.6,
+ "position": [
+ -2432,
+ -80
+ ],
+ "id": "dde4054e-d2d2-4d79-be5d-ab1f772e23e7",
+ "name": "Get pending PODs",
+ "credentials": {
+ "postgres": {
+ "id": "pxziuCOuNpsUFKrm",
+ "name": "transpas_n8n_gheeraert"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "url": "={{ $json.tracking_url }}",
+ "options": {
+ "response": {
+ "response": {
+ "responseFormat": "file"
+ }
+ }
+ }
+ },
+ "type": "n8n-nodes-base.httpRequest",
+ "typeVersion": 4.4,
+ "position": [
+ -2192,
+ -80
+ ],
+ "id": "a94882b8-2b42-4a17-ae8a-0f3df1196a0c",
+ "name": "Download POD",
+ "onError": "continueErrorOutput"
+ },
+ {
+ "parameters": {
+ "jsCode": "function safeFilePart(value) {\n return String(value || '')\n .replace(/[^a-zA-Z0-9._-]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .slice(0, 80) || 'pod';\n}\n\nfunction nowStampFromInput(input) {\n const d = input ? new Date(input) : new Date();\n const dt = isNaN(d.getTime()) ? new Date() : d;\n const p = (n) => String(n).padStart(2, '0');\n return `${dt.getFullYear()}${p(dt.getMonth() + 1)}${p(dt.getDate())}_${p(dt.getHours())}${p(dt.getMinutes())}${p(dt.getSeconds())}`;\n}\n\nfunction xmlEsc(value) {\n return String(value ?? '')\n .replace(/&(?!amp;|lt;|gt;|quot;|apos;|#\\d+;|#x[0-9A-Fa-f]+;)/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction formatATDateTime(input) {\n if (!input) return '';\n const d = new Date(input);\n if (isNaN(d.getTime())) return '';\n const pad = (n) => String(n).padStart(2, '0');\n return (\n d.getFullYear() +\n pad(d.getMonth() + 1) +\n pad(d.getDate()) +\n pad(d.getHours()) +\n pad(d.getMinutes()) +\n pad(d.getSeconds())\n );\n}\n\nconst j = $json;\nif (!j.tracking_url) {\n throw new Error('tracking_url ontbreekt');\n}\nif (!$binary?.data) {\n throw new Error('Geen binary data gevonden in HTTP download');\n}\n\nlet ext = 'bin';\nlet baseName = '';\ntry {\n const u = new URL(j.tracking_url);\n const pathname = u.pathname || '';\n const parts = pathname.split('/').filter(Boolean);\n baseName = parts.length ? parts[parts.length - 1] : '';\n const m = pathname.match(/\\.([A-Za-z0-9]+)$/);\n if (m) ext = m[1].toLowerCase();\n} catch (e) {}\n\nconst podFileName = `GHE_POD_${safeFilePart(j.purchase_order_number)}_${nowStampFromInput(j.tracking_date)}.${ext}`;\nconst transpasXmlFile = `GHE_POD_XML_${safeFilePart(j.purchase_order_number)}_${nowStampFromInput(j.tracking_date)}.xml`;\nconst atDateTime = formatATDateTime(j.tracking_date) || formatATDateTime(new Date().toISOString());\n\nconst binary = $binary.data;\nbinary.fileName = podFileName;\nbinary.mimeType = binary.mimeType || 'application/octet-stream';\nconst base64Data = binary.data;\n\nconst originalName = baseName || podFileName;\nconst fileType = ext || 'bin';\nconst note = [\n 'POD Online',\n j.tracking_comments || '',\n j.tracking_url ? `URL: ${j.tracking_url}` : '',\n].filter(Boolean).join(' - ');\n\nconst transpasXml = [\n '',\n '',\n ` ${xmlEsc(atDateTime)}`,\n ' TLN',\n ' ',\n ` ${xmlEsc(j.purchase_order_number)}`,\n ' 35',\n ` ${xmlEsc(note)}`,\n ` ${base64Data}`,\n ` ${xmlEsc(originalName)}`,\n ' ',\n ''\n].join('\\n');\n\nreturn {\n json: {\n ...j,\n pod_file_name: podFileName,\n transpas_xml: transpasXml,\n transpas_xml_file: transpasXmlFile,\n },\n binary: {\n data: binary,\n },\n};"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ -1952,
+ -80
+ ],
+ "id": "bc443c37-8620-4c4a-a37d-7c2632a30827",
+ "name": "Build POD Transpas XML"
+ },
+ {
+ "parameters": {
+ "jsCode": "const xml = $json.transpas_xml;\nconst fileName = $json.transpas_xml_file || 'pod_status.xml';\n\nconst binaryData = await this.helpers.prepareBinaryData(\n Buffer.from(xml, 'utf8'),\n fileName,\n 'application/xml'\n);\n\nreturn {\n json: {\n ...$json,\n },\n binary: {\n data: binaryData,\n },\n};"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ -1712,
+ -80
+ ],
+ "id": "6c04eb86-5476-473a-a911-ced8dac43c69",
+ "name": "Create POD XML Binary"
+ },
+ {
+ "parameters": {
+ "protocol": "sftp",
+ "operation": "upload",
+ "path": "=/dewit/pod_xml/{{ $json.transpas_xml_file }}",
+ "options": {}
+ },
+ "type": "n8n-nodes-base.ftp",
+ "typeVersion": 1,
+ "position": [
+ -1472,
+ -80
+ ],
+ "id": "c5300449-3418-4f22-99e0-f017dc3e167d",
+ "name": "SFTP Upload POD XML",
+ "credentials": {
+ "sftp": {
+ "id": "uKyzg5cSXQqXOuHI",
+ "name": "SFTP n8n"
+ }
+ },
+ "onError": "continueErrorOutput"
+ },
+ {
+ "parameters": {
+ "operation": "executeQuery",
+ "query": "UPDATE public.ghe_status_log\nSET\n pod_downloaded = true,\n pod_file_name = $1::varchar,\n pod_downloaded_at = CURRENT_TIMESTAMP,\n transpas_xml_file = $2::varchar,\n transpas_xml_created_at = CURRENT_TIMESTAMP,\n transpas_export_status = 'exported',\n transpas_export_error = NULL\nWHERE id = $3::bigint;",
+ "options": {
+ "queryReplacement": "={{ [$json.pod_file_name, $json.transpas_xml_file, $json.id] }}"
+ }
+ },
+ "type": "n8n-nodes-base.postgres",
+ "typeVersion": 2.6,
+ "position": [
+ -1232,
+ -144
+ ],
+ "id": "117c6ad2-aa7c-41aa-ae45-bf840867fd3b",
+ "name": "Mark POD Exported",
+ "credentials": {
+ "postgres": {
+ "id": "pxziuCOuNpsUFKrm",
+ "name": "transpas_n8n_gheeraert"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "operation": "executeQuery",
+ "query": "UPDATE public.ghe_order_log\nSET\n pod_downloaded_at = CURRENT_TIMESTAMP,\n next_status_poll_at = CURRENT_TIMESTAMP + INTERVAL '1 day'\nWHERE id = $1::bigint;",
+ "options": {
+ "queryReplacement": "={{ [$json.order_log_id] }}"
+ }
+ },
+ "type": "n8n-nodes-base.postgres",
+ "typeVersion": 2.6,
+ "position": [
+ -992,
+ -144
+ ],
+ "id": "1e1a3c7e-189d-4cb0-89c3-a200409c936d",
+ "name": "Update ghe_order_log POD",
+ "credentials": {
+ "postgres": {
+ "id": "pxziuCOuNpsUFKrm",
+ "name": "transpas_n8n_gheeraert"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "operation": "executeQuery",
+ "query": "WITH input AS (\n SELECT $1::jsonb AS j\n)\nUPDATE public.ghe_status_log\nSET\n transpas_export_status = 'error',\n transpas_export_error = COALESCE(NULLIF(transpas_export_error, ''), '') || CASE WHEN COALESCE(NULLIF(transpas_export_error, ''), '') = '' THEN '' ELSE ' | ' END || (j->>'error_text')::text\nFROM input\nWHERE public.ghe_status_log.id = (j->>'id')::bigint;",
+ "options": {
+ "queryReplacement": "={{ [JSON.stringify({ id: $json.id, error_text: $json.error_text || 'POD download/upload/XML verwerking mislukt' })] }}"
+ }
+ },
+ "type": "n8n-nodes-base.postgres",
+ "typeVersion": 2.6,
+ "position": [
+ -1472,
+ 80
+ ],
+ "id": "0a50219a-8844-49fc-89d1-a60cb38d07c3",
+ "name": "Log POD Error",
+ "credentials": {
+ "postgres": {
+ "id": "pxziuCOuNpsUFKrm",
+ "name": "transpas_n8n_gheeraert"
+ }
+ }
+ },
+ {
+ "parameters": {
+ "jsCode": "const err = $json.error || {};\nconst message = err.message || $json.message || 'POD download/upload/XML verwerking mislukt';\nreturn { json: { ...$json, error_text: message } };"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ -1952,
+ 80
+ ],
+ "id": "6aad54d4-797f-4713-83f2-1f61d1600a7b",
+ "name": "Prepare POD Error"
+ },
+ {
+ "parameters": {
+ "jsCode": "const err = $json.error || {};\nconst message = err.message || $json.message || 'SFTP upload POD XML mislukt';\nreturn { json: { ...$json, error_text: message } };"
+ },
+ "type": "n8n-nodes-base.code",
+ "typeVersion": 2,
+ "position": [
+ -1232,
+ 80
+ ],
+ "id": "895e1542-95c2-40a6-9101-295480730af2",
+ "name": "Prepare SFTP Error"
+ },
+ {
+ "parameters": {
+ "rule": {
+ "interval": [
+ {
+ "field": "cronExpression",
+ "expression": "*/20 6-22 * * 1-5"
+ }
+ ]
+ }
+ },
+ "type": "n8n-nodes-base.scheduleTrigger",
+ "typeVersion": 1.3,
+ "position": [
+ -2640,
+ -80
+ ],
+ "id": "1917c911-de8d-4c6b-8d71-cd0884573a08",
+ "name": "Schedule Trigger"
+ },
+ {
+ "parameters": {
+ "protocol": "sftp",
+ "operation": "list",
+ "options": {}
+ },
+ "type": "n8n-nodes-base.ftp",
+ "typeVersion": 1,
+ "position": [
+ -992,
+ 64
+ ],
+ "id": "8f75c195-722e-4e8c-83ad-19d329cda22e",
+ "name": "FTP",
+ "credentials": {
+ "sftp": {
+ "id": "uKyzg5cSXQqXOuHI",
+ "name": "SFTP n8n"
+ }
+ }
+ }
+ ],
+ "pinData": {},
+ "connections": {
+ "Get pending PODs": {
+ "main": [
+ [
+ {
+ "node": "Download POD",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Download POD": {
+ "main": [
+ [
+ {
+ "node": "Build POD Transpas XML",
+ "type": "main",
+ "index": 0
+ }
+ ],
+ [
+ {
+ "node": "Prepare POD Error",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Prepare POD Error": {
+ "main": [
+ [
+ {
+ "node": "Log POD Error",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Build POD Transpas XML": {
+ "main": [
+ [
+ {
+ "node": "Create POD XML Binary",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Create POD XML Binary": {
+ "main": [
+ [
+ {
+ "node": "SFTP Upload POD XML",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "SFTP Upload POD XML": {
+ "main": [
+ [
+ {
+ "node": "Mark POD Exported",
+ "type": "main",
+ "index": 0
+ },
+ {
+ "node": "FTP",
+ "type": "main",
+ "index": 0
+ }
+ ],
+ [
+ {
+ "node": "Prepare SFTP Error",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Prepare SFTP Error": {
+ "main": [
+ [
+ {
+ "node": "Log POD Error",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Mark POD Exported": {
+ "main": [
+ [
+ {
+ "node": "Update ghe_order_log POD",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ },
+ "Schedule Trigger": {
+ "main": [
+ [
+ {
+ "node": "Get pending PODs",
+ "type": "main",
+ "index": 0
+ }
+ ]
+ ]
+ }
+ },
+ "active": false,
+ "settings": {
+ "executionOrder": "v1",
+ "binaryMode": "separate"
+ },
+ "versionId": "1761ac71-29de-4791-880a-4d790f991107",
+ "meta": {
+ "templateCredsSetupCompleted": true,
+ "instanceId": "bef8d409866a58c0777dfe7cca1b9c2400fd051c056d361501393ab423006b5f"
+ },
+ "nodeGroups": [],
+ "id": "CsLGuXIeZQszqTPz",
+ "tags": []
+}
\ No newline at end of file