From 2b98b3dad30dddf085586ff9639d76e3e8681538 Mon Sep 17 00:00:00 2001 From: adminjeroen Date: Sat, 4 Jul 2026 21:46:53 +0200 Subject: [PATCH] Haalt laatste status ook op --- .../Palletways BI - Import Statussen.json | 227 ++++++++++-------- 1 file changed, 129 insertions(+), 98 deletions(-) diff --git a/palletways/Palletways BI - Import Statussen.json b/palletways/Palletways BI - Import Statussen.json index 6fd90c4..3c78196 100644 --- a/palletways/Palletways BI - Import Statussen.json +++ b/palletways/Palletways BI - Import Statussen.json @@ -12,28 +12,49 @@ ] } }, - "id": "5031836c-8cf3-4375-9fbc-4abe47e6219d", + "id": "99e52813-bb87-4d86-b436-59ad811a5983", "name": "Schedule Trigger - elke 2 uur", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.2, "position": [ - -1312, - 688 + 1056, + 1792 ] }, { "parameters": { "operation": "executeQuery", - "query": "SELECT\n tracking_id,\n COALESCE(con_no, NULL) AS con_no,\n is_collection_role,\n is_delivery_role,\n is_manifest_role,\n is_manifested,\n is_delivered,\n is_deleted,\n is_never_arrived,\n current_status,\n last_status_poll_at\nFROM palletways_shipments\nWHERE tracking_id IS NOT NULL\nAND COALESCE(status_poll_done, false) = false\nAND (\n (\n is_collection_role = true\n AND COALESCE(is_delivery_role, false) = false\n AND COALESCE(is_manifested, false) = false\n )\n OR\n (\n (\n COALESCE(is_delivery_role, false) = true\n OR COALESCE(is_manifest_role, false) = true\n OR COALESCE(is_collection_role, false) = false\n )\n AND COALESCE(is_delivered, false) = false\n AND COALESCE(is_deleted, false) = false\n AND COALESCE(is_never_arrived, false) = false\n AND COALESCE(current_status, '') NOT IN (\n 'JOB COMPLETE',\n 'DELIVERED',\n 'DELETED',\n 'NEVER ARRIVED'\n )\n )\n)\nAND (\n next_status_poll_at IS NULL\n OR next_status_poll_at <= now()\n)\nORDER BY COALESCE(last_status_poll_at, '2000-01-01'::timestamp) ASC\nLIMIT 500;", + "query": "ALTER TABLE palletways_shipments\nADD COLUMN IF NOT EXISTS summary_status text,\nADD COLUMN IF NOT EXISTS summary_status_at timestamp,\nADD COLUMN IF NOT EXISTS last_note_status text,\nADD COLUMN IF NOT EXISTS last_note_text text,\nADD COLUMN IF NOT EXISTS last_note_at timestamp;\n\nALTER TABLE palletways_status_events\nADD COLUMN IF NOT EXISTS interpreted_status text,\nADD COLUMN IF NOT EXISTS status_phase text,\nADD COLUMN IF NOT EXISTS status_priority int;\n\nCREATE INDEX IF NOT EXISTS idx_pw_status_events_tracking\nON palletways_status_events (tracking_id);\n\nCREATE INDEX IF NOT EXISTS idx_pw_status_events_tracking_event\nON palletways_status_events (tracking_id, event_at DESC);\n\nCREATE INDEX IF NOT EXISTS idx_pw_shipments_poll\nON palletways_shipments (status_poll_done, next_status_poll_at);\n\nCREATE INDEX IF NOT EXISTS idx_pw_shipments_current_status\nON palletways_shipments (current_status);", "options": {} }, - "id": "5d5132f1-03b1-4c8c-879a-e318eabc0c65", + "id": "8a8f7096-4585-4f3e-a460-542cb94e0b03", + "name": "Postgres - Setup columns and indexes", + "type": "n8n-nodes-base.postgres", + "typeVersion": 2.6, + "position": [ + 1280, + 1792 + ], + "credentials": { + "postgres": { + "id": "a0jnANQ89VLSby0b", + "name": "Palletways DB" + } + } + }, + { + "parameters": { + "operation": "executeQuery", + "query": "SELECT\n tracking_id,\n COALESCE(con_no, consignment_number) AS con_no,\n is_collection_role,\n is_delivery_role,\n is_manifest_role,\n is_manifested,\n is_delivered,\n is_deleted,\n is_never_arrived,\n current_status,\n last_status_poll_at\nFROM palletways_shipments\nWHERE tracking_id IS NOT NULL\nAND COALESCE(status_poll_done, false) = false\nAND (\n next_status_poll_at IS NULL\n OR next_status_poll_at <= now()\n)\nAND COALESCE(is_deleted, false) = false\nAND COALESCE(is_never_arrived, false) = false\nAND upper(COALESCE(current_status, '')) NOT IN (\n 'DELETED',\n 'DELETE',\n 'JOB DELETED',\n 'CANCELLED',\n 'CANCELED',\n 'NEVER ARRIVED',\n 'NEVERARRIVED',\n 'JOB COMPLETE',\n 'DELIVERED'\n)\nORDER BY COALESCE(last_status_poll_at, '2000-01-01'::timestamp) ASC\nLIMIT 500;", + "options": {} + }, + "id": "460ca8cd-328e-4bad-b5e8-960407c9bfbb", "name": "Postgres - Select shipments to poll", "type": "n8n-nodes-base.postgres", "typeVersion": 2.6, "position": [ - -1104, - 688 + 1504, + 1792 ], "credentials": { "postgres": { @@ -46,31 +67,31 @@ "parameters": { "options": {} }, - "id": "013f9249-a851-4d7b-a9b9-e7e250c34839", + "id": "1aed29a8-70bc-4196-9bb8-7edd842a26c4", "name": "Loop Over Shipments", "type": "n8n-nodes-base.splitInBatches", "typeVersion": 3, "position": [ - -880, - 688 + 1728, + 1792 ] }, { "parameters": { - "jsCode": "const apiKey = 'SXJaM7PLjZDqfsnXSDP8Y9wDY6crxJySBg705MQEPus%3D';\nconst baseUrl = 'https://api.palletways.com/getnotes/trackingid';\n\nconst trackingId = String($json.tracking_id);\n\nreturn [{\n json: {\n ...$json,\n api_key: apiKey,\n status_url: `${baseUrl}/${encodeURIComponent(trackingId)}?apikey=${apiKey}`\n }\n}];" + "jsCode": "const apiKey = 'SXJaM7PLjZDqfsnXSDP8Y9wDY6crxJySBg705MQEPus%3D';\n\nconst trackingId = String($json.tracking_id);\n\nreturn [{\n json: {\n ...$json,\n api_key: apiKey,\n notes_url: `https://api.palletways.com/getnotes/trackingid/${encodeURIComponent(trackingId)}?apikey=${apiKey}`,\n summary_url: `https://api.palletways.com/gettrackingnotes/${encodeURIComponent(trackingId)}?apikey=${apiKey}`,\n }\n}];" }, - "id": "052f9605-e43b-42c8-89aa-70a044cf7931", - "name": "Code - Build Palletways URL", + "id": "08ec873b-e824-4ade-9ff4-43f485b479e0", + "name": "Code - Build URLs", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ - -656, - 560 + 1936, + 1648 ] }, { "parameters": { - "url": "={{ $json.status_url }}", + "url": "={{ $json.notes_url }}", "options": { "response": { "response": { @@ -80,54 +101,41 @@ "timeout": 120000 } }, - "id": "a025af41-cdfe-414b-b132-1244ae3bf316", - "name": "HTTP - Get Palletways notes", + "id": "3ec6f3c9-2236-4dae-a219-617d270d6add", + "name": "HTTP - Get detailed notes", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ - -432, - 560 + 2160, + 1648 ] }, { "parameters": { - "options": {} + "jsCode": "const shipment = $('Code - Build URLs').item.json;\n\nfunction getXmlFromJson(j) {\n if (typeof j === 'string') return j;\n if (typeof j.data === 'string') return j.data;\n if (typeof j.body === 'string') return j.body;\n if (typeof j.response === 'string') return j.response;\n\n for (const v of Object.values(j)) {\n if (typeof v === 'string' && v.trim().startsWith('<')) return v;\n }\n\n return JSON.stringify(j);\n}\n\nfunction xmlUnescape(s) {\n if (s === null || s === undefined) return null;\n return String(s)\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/&/g, '&')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\");\n}\n\nfunction tag(block, name) {\n const re = new RegExp(`<${name}>([\\\\s\\\\S]*?)<\\\\/${name}>`, 'i');\n const m = String(block || '').match(re);\n return m ? xmlUnescape(m[1]).trim() : null;\n}\n\nfunction dataBlocks(xml) {\n const blocks = [];\n const re = /([\\s\\S]*?)<\\/Data>/gi;\n let m;\n while ((m = re.exec(String(xml || ''))) !== null) {\n blocks.push(m[1]);\n }\n return blocks;\n}\n\nfunction emptyToNull(v) {\n if (v === undefined || v === null || v === '') return null;\n return v;\n}\n\nfunction toInt(v) {\n if (v === undefined || v === null || v === '') return null;\n const n = Number(v);\n return Number.isFinite(n) ? n : null;\n}\n\nfunction makeTimestamp(date, time) {\n if (!date) return null;\n return `${date} ${time || '00:00:00'}`;\n}\n\nconst xml = getXmlFromJson($json);\n\nconst events = dataBlocks(xml).map(row => {\n const noteDate = emptyToNull(tag(row, 'NoteDate'));\n const noteTime = emptyToNull(tag(row, 'NoteTime'));\n\n return {\n tracking_id: String(tag(row, 'TrackingID') || shipment.tracking_id || ''),\n con_no: emptyToNull(tag(row, 'ConNo') || shipment.con_no),\n delta: toInt(tag(row, 'Delta')),\n sysnote: emptyToNull(tag(row, 'SysNote')),\n barcode: emptyToNull(tag(row, 'Barcode')),\n\n event_at: makeTimestamp(noteDate, noteTime),\n note_date: noteDate,\n note_time: noteTime,\n\n pod_date: emptyToNull(tag(row, 'PodDate')),\n pod_time: emptyToNull(tag(row, 'PodTime')),\n pod_signature: emptyToNull(tag(row, 'PodSignature')),\n\n bookin_date: emptyToNull(tag(row, 'BookInDate')),\n bookin_time: emptyToNull(tag(row, 'BookInTime')),\n bookin_contact: emptyToNull(tag(row, 'BookInContact')),\n bookin_reference: emptyToNull(tag(row, 'BookInReference')),\n\n eta_date: emptyToNull(tag(row, 'ETADate')),\n eta_time: emptyToNull(tag(row, 'ETATime')),\n\n status_code: toInt(tag(row, 'StatusCode')),\n paying_depot: emptyToNull(tag(row, 'PayingDepot')),\n note_group: emptyToNull(tag(row, 'NoteGroup')),\n note_type: emptyToNull(tag(row, 'NoteType')),\n note_code_id: toInt(tag(row, 'NoteCodeID')),\n note_text: emptyToNull(tag(row, 'NoteText')),\n\n raw_json: {\n raw_xml: row\n }\n };\n}).filter(e => e.tracking_id && (e.sysnote || e.note_text || e.note_code_id !== null || e.status_code !== null));\n\nreturn [{\n json: {\n ...shipment,\n events_json: JSON.stringify(events),\n event_count: events.length,\n }\n}];" }, - "id": "1637a055-5c34-4498-bb8b-5b85cad4b90c", - "name": "XML - Parse notes response", - "type": "n8n-nodes-base.xml", - "typeVersion": 1, - "position": [ - -224, - 560 - ] - }, - { - "parameters": { - "jsCode": "const shipment = $('Code - Build Palletways URL').item.json;\n\nconst response = $json.Response ?? $json;\nconst status = response.Status ?? {};\nconst detail = response.Detail ?? {};\n\nlet data = detail.Data ?? [];\n\nif (!Array.isArray(data)) {\n data = data ? [data] : [];\n}\n\nfunction emptyToNull(value) {\n if (value === undefined || value === null || value === '') return null;\n return value;\n}\n\nfunction toInt(value) {\n if (value === undefined || value === null || value === '') return null;\n const n = Number(value);\n return Number.isFinite(n) ? n : null;\n}\n\nfunction makeTimestamp(date, time) {\n if (!date) return null;\n const t = time || '00:00:00';\n return `${date} ${t}`;\n}\n\nif ((status.Code ?? '').toUpperCase() !== 'OK') {\n return [{\n json: {\n tracking_id: shipment.tracking_id,\n con_no: shipment.con_no,\n api_ok: false,\n api_status_code: status.Code ?? null,\n api_status_description: status.Description ?? null,\n error_text: `Palletways API status niet OK: ${status.Code ?? ''} ${status.Description ?? ''}`,\n has_event: false\n }\n }];\n}\n\nif (data.length === 0) {\n return [{\n json: {\n tracking_id: shipment.tracking_id,\n con_no: shipment.con_no,\n api_ok: true,\n has_event: false,\n no_events: true\n }\n }];\n}\n\nreturn data.map(row => {\n const noteDate = emptyToNull(row.NoteDate);\n const noteTime = emptyToNull(row.NoteTime);\n\n return {\n json: {\n tracking_id: String(row.TrackingID ?? shipment.tracking_id ?? ''),\n con_no: emptyToNull(row.ConNo ?? shipment.con_no),\n delta: toInt(row.Delta),\n sysnote: emptyToNull(row.SysNote),\n barcode: emptyToNull(row.Barcode),\n\n event_at: makeTimestamp(noteDate, noteTime),\n note_date: noteDate,\n note_time: noteTime,\n\n pod_date: emptyToNull(row.PodDate),\n pod_time: emptyToNull(row.PodTime),\n pod_signature: emptyToNull(row.PodSignature),\n\n bookin_date: emptyToNull(row.BookInDate),\n bookin_time: emptyToNull(row.BookInTime),\n bookin_contact: emptyToNull(row.BookInContact),\n bookin_reference: emptyToNull(row.BookInReference),\n\n eta_date: emptyToNull(row.ETADate),\n eta_time: emptyToNull(row.ETATime),\n\n status_code: toInt(row.StatusCode),\n paying_depot: emptyToNull(row.PayingDepot),\n note_group: emptyToNull(row.NoteGroup),\n note_type: emptyToNull(row.NoteType),\n note_code_id: toInt(row.NoteCodeID),\n note_text: emptyToNull(row.NoteText),\n\n raw_json: row,\n api_ok: true,\n has_event: true\n }\n };\n});" - }, - "id": "ba94d04c-0e30-40ca-98ff-f74c6e3eecad", - "name": "Code - Normalize status events", + "id": "ef6367ca-1d6c-43a6-bea4-0795e47693fa", + "name": "Code - Parse notes", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ - 16, - 560 + 2384, + 1648 ] }, { "parameters": { "operation": "executeQuery", - "query": "INSERT INTO palletways_status_events (\n tracking_id,\n con_no,\n delta,\n sysnote,\n barcode,\n event_at,\n note_date,\n note_time,\n pod_date,\n pod_time,\n pod_signature,\n bookin_date,\n bookin_time,\n bookin_contact,\n bookin_reference,\n eta_date,\n eta_time,\n status_code,\n paying_depot,\n note_group,\n note_type,\n note_code_id,\n note_text,\n raw_json\n)\nSELECT\n '{{ $json.tracking_id }}',\n NULLIF('{{ ($json.con_no ?? \"\").toString().replace(/'/g, \"''\") }}', ''),\n {{ $json.delta ?? 'NULL' }},\n NULLIF('{{ ($json.sysnote ?? \"\").toString().replace(/'/g, \"''\") }}', ''),\n NULLIF('{{ ($json.barcode ?? \"\").toString().replace(/'/g, \"''\") }}', ''),\n {{ $json.event_at ? \"'\" + $json.event_at + \"'\" : \"NULL\" }},\n {{ $json.note_date ? \"'\" + $json.note_date + \"'\" : \"NULL\" }},\n {{ $json.note_time ? \"'\" + $json.note_time + \"'\" : \"NULL\" }},\n {{ $json.pod_date ? \"'\" + $json.pod_date + \"'\" : \"NULL\" }},\n {{ $json.pod_time ? \"'\" + $json.pod_time + \"'\" : \"NULL\" }},\n NULLIF('{{ ($json.pod_signature ?? \"\").toString().replace(/'/g, \"''\") }}', ''),\n {{ $json.bookin_date ? \"'\" + $json.bookin_date + \"'\" : \"NULL\" }},\n {{ $json.bookin_time ? \"'\" + $json.bookin_time + \"'\" : \"NULL\" }},\n NULLIF('{{ ($json.bookin_contact ?? \"\").toString().replace(/'/g, \"''\") }}', ''),\n NULLIF('{{ ($json.bookin_reference ?? \"\").toString().replace(/'/g, \"''\") }}', ''),\n {{ $json.eta_date ? \"'\" + $json.eta_date + \"'\" : \"NULL\" }},\n {{ $json.eta_time ? \"'\" + $json.eta_time + \"'\" : \"NULL\" }},\n {{ $json.status_code ?? 'NULL' }},\n NULLIF('{{ ($json.paying_depot ?? \"\").toString().replace(/'/g, \"''\") }}', ''),\n NULLIF('{{ ($json.note_group ?? \"\").toString().replace(/'/g, \"''\") }}', ''),\n NULLIF('{{ ($json.note_type ?? \"\").toString().replace(/'/g, \"''\") }}', ''),\n {{ $json.note_code_id ?? 'NULL' }},\n NULLIF('{{ ($json.note_text ?? \"\").toString().replace(/'/g, \"''\") }}', ''),\n '{{ JSON.stringify($json.raw_json ?? {}).replace(/'/g, \"''\") }}'::jsonb\nWHERE {{ $json.has_event ? 'true' : 'false' }}\nON CONFLICT (tracking_id, sysnote) DO NOTHING;", + "query": "WITH input AS (\n SELECT\n '{{ $json.tracking_id }}'::text AS tracking_id,\n NULLIF('{{ ($json.con_no ?? \"\").toString().replace(/'/g, \"''\") }}', '')::text AS con_no,\n '{{ ($json.notes_url ?? \"\").toString().replace(/'/g, \"''\") }}'::text AS notes_url,\n '{{ ($json.summary_url ?? \"\").toString().replace(/'/g, \"''\") }}'::text AS summary_url,\n '{{ ($json.events_json ?? \"[]\").replace(/'/g, \"''\") }}'::jsonb AS events_json\n),\n\nevents_raw AS (\n SELECT\n x.*\n FROM input i\n CROSS JOIN LATERAL jsonb_to_recordset(i.events_json) AS x(\n tracking_id text,\n con_no text,\n delta bigint,\n sysnote text,\n barcode text,\n event_at timestamp,\n note_date date,\n note_time time,\n pod_date date,\n pod_time time,\n pod_signature text,\n bookin_date date,\n bookin_time time,\n bookin_contact text,\n bookin_reference text,\n eta_date date,\n eta_time time,\n status_code int,\n paying_depot text,\n note_group text,\n note_type text,\n note_code_id int,\n note_text text,\n raw_json jsonb\n )\n),\n\nevents_deduplicated AS (\n SELECT\n tracking_id,\n con_no,\n delta,\n sysnote,\n barcode,\n event_at,\n note_date,\n note_time,\n pod_date,\n pod_time,\n pod_signature,\n bookin_date,\n bookin_time,\n bookin_contact,\n bookin_reference,\n eta_date,\n eta_time,\n status_code,\n paying_depot,\n note_group,\n note_type,\n note_code_id,\n note_text,\n raw_json\n FROM (\n SELECT\n e.*,\n ROW_NUMBER() OVER (\n PARTITION BY\n e.tracking_id,\n COALESCE(e.sysnote, ''),\n COALESCE(e.note_group, ''),\n COALESCE(e.note_type, ''),\n COALESCE(e.note_code_id::text, ''),\n COALESCE(e.status_code::text, ''),\n COALESCE(e.event_at::text, ''),\n COALESCE(e.note_text, '')\n ORDER BY\n e.event_at DESC NULLS LAST,\n e.delta DESC NULLS LAST,\n e.note_text DESC NULLS LAST\n ) AS rn\n FROM events_raw e\n WHERE e.tracking_id IS NOT NULL\n ) ranked\n WHERE rn = 1\n),\n\ninsert_events AS (\n INSERT INTO palletways_status_events (\n tracking_id,\n con_no,\n delta,\n sysnote,\n barcode,\n event_at,\n note_date,\n note_time,\n pod_date,\n pod_time,\n pod_signature,\n bookin_date,\n bookin_time,\n bookin_contact,\n bookin_reference,\n eta_date,\n eta_time,\n status_code,\n paying_depot,\n note_group,\n note_type,\n note_code_id,\n note_text,\n raw_json\n )\n SELECT\n e.tracking_id,\n e.con_no,\n e.delta,\n e.sysnote,\n e.barcode,\n e.event_at,\n e.note_date,\n e.note_time,\n e.pod_date,\n e.pod_time,\n e.pod_signature,\n e.bookin_date,\n e.bookin_time,\n e.bookin_contact,\n e.bookin_reference,\n e.eta_date,\n e.eta_time,\n e.status_code,\n e.paying_depot,\n e.note_group,\n e.note_type,\n e.note_code_id,\n e.note_text,\n e.raw_json\n FROM events_deduplicated e\n ON CONFLICT DO NOTHING\n RETURNING tracking_id\n),\n\nclassify AS (\n UPDATE palletways_status_events e\n SET\n interpreted_status = CASE\n\n WHEN e.note_group = 'JIN'\n AND e.note_type = 'IIN'\n AND e.note_code_id = 52001\n AND (\n e.note_text ILIKE '%successfully deleted%'\n OR e.note_text ILIKE '%consignment%deleted%'\n OR e.note_text ILIKE '%zending%verwijderd%'\n )\n THEN 'DELETED'\n\n WHEN e.note_group = 'JIN'\n AND e.note_type = 'COLRQ'\n AND e.note_code_id = 353999\n THEN 'COLLECTION REQUESTED'\n\n WHEN e.note_group = 'JIN'\n AND e.note_type = 'COLAC'\n AND e.note_code_id = 352999\n THEN 'COLLECTION ACCEPTED'\n\n WHEN e.note_group = 'POC'\n AND e.note_type = 'POC'\n AND e.note_code_id = 219999\n THEN 'COLLECTED'\n\n WHEN e.note_group = 'JIN'\n AND e.note_type = 'IIN'\n AND e.note_code_id = 52001\n AND (\n e.note_text ILIKE '%Verzonden%'\n OR e.note_text ILIKE '%Shipped%'\n )\n THEN 'MANIFESTED'\n\n WHEN e.status_code = 300\n AND e.note_group = 'VL'\n AND e.note_type = 'SCN'\n AND e.note_code_id = 48001\n THEN 'LOADED COLLECTION VEHICLE'\n\n WHEN e.status_code = 500\n AND e.note_group = 'VU'\n AND e.note_type = 'SCN'\n AND e.note_code_id = 49001\n THEN 'UNLOADED AT HUB'\n\n WHEN e.status_code = 500\n AND e.note_group = 'BAY'\n AND e.note_type = 'SCN'\n AND e.note_code_id = 47001\n THEN 'SCANNED IN BAY'\n\n WHEN e.note_group = 'LH'\n AND e.note_type = 'LLD'\n AND e.note_code_id = 101999\n THEN 'LINEHAUL LOAD'\n\n WHEN e.note_group = 'LH'\n AND e.note_type = 'LUL'\n AND e.note_code_id = 102999\n THEN 'LINEHAUL UNLOAD'\n\n WHEN e.note_group = 'LH'\n AND e.note_type = 'LBA'\n AND e.note_code_id = 103999\n THEN 'LINEHAUL BAYCHECK'\n\n WHEN e.status_code = 550\n AND e.note_group = 'JIN'\n AND e.note_type = 'GDEP'\n AND e.note_code_id = 348999\n THEN 'LEFT HUB'\n\n WHEN e.status_code = 700\n AND e.note_group = 'VU'\n AND e.note_type = 'SCN'\n AND e.note_code_id = 49001\n THEN 'AT DELIVERY DEPOT'\n\n WHEN e.status_code = 800\n AND e.note_group = 'VL'\n AND e.note_type = 'SCN'\n AND e.note_code_id = 48001\n THEN 'OUT FOR DELIVERY'\n\n WHEN e.note_group = 'VP'\n AND e.note_type = 'PDT'\n AND e.note_code_id = 218999\n THEN 'ETA PLANNED'\n\n WHEN e.note_group = 'VP'\n AND e.note_type = 'DAR'\n AND e.note_code_id = 307999\n THEN 'DRIVER ARRIVED'\n\n WHEN e.status_code = 900\n AND e.note_group = 'POD'\n AND e.note_type = 'POD'\n AND e.note_code_id = 36\n THEN 'DELIVERED'\n\n WHEN e.note_group = 'POD'\n AND e.note_type = 'IMG'\n AND e.note_code_id = 67999\n THEN 'POD IMAGE'\n\n WHEN e.note_group = 'BIN'\n AND e.note_type = 'BIN'\n AND e.note_code_id = 32\n THEN 'BOOKED IN'\n\n WHEN e.note_group = 'CUSV'\n AND e.note_type = 'CUSN'\n AND e.note_code_id = 297999\n THEN 'CUSTOMER SERVICE NOTE'\n\n WHEN e.note_group = 'JIN'\n AND e.note_type IN ('IIN', 'GIN')\n THEN 'GENERAL INFORMATION'\n\n ELSE 'UNKNOWN'\n END,\n\n status_phase = CASE\n\n WHEN (\n e.note_text ILIKE '%successfully deleted%'\n OR e.note_text ILIKE '%consignment%deleted%'\n OR e.note_text ILIKE '%zending%verwijderd%'\n )\n THEN 'TERMINAL'\n\n WHEN e.note_group = 'JIN'\n AND e.note_type IN ('COLRQ', 'COLAC')\n THEN 'COLLECTION_PLANNING'\n\n WHEN e.note_group = 'POC'\n AND e.note_type = 'POC'\n THEN 'COLLECTED'\n\n WHEN e.note_group = 'JIN'\n AND e.note_type = 'IIN'\n AND e.note_code_id = 52001\n AND (\n e.note_text ILIKE '%Verzonden%'\n OR e.note_text ILIKE '%Shipped%'\n )\n THEN 'MANIFESTED'\n\n WHEN e.status_code IN (300, 500, 525, 530, 550)\n OR e.note_group IN ('LH', 'BAY')\n THEN 'IN_NETWORK'\n\n WHEN e.status_code = 700\n THEN 'DELIVERY_DEPOT'\n\n WHEN e.status_code = 800\n OR e.note_group = 'VP'\n THEN 'DELIVERY_ROUTE'\n\n WHEN e.status_code = 900\n AND e.note_group = 'POD'\n THEN 'DELIVERED'\n\n WHEN e.note_group = 'BIN'\n THEN 'BOOKING'\n\n ELSE 'INFO'\n END,\n\n status_priority = CASE\n\n WHEN (\n e.note_text ILIKE '%successfully deleted%'\n OR e.note_text ILIKE '%consignment%deleted%'\n OR e.note_text ILIKE '%zending%verwijderd%'\n )\n THEN 110\n\n WHEN e.status_code = 900\n AND e.note_group = 'POD'\n THEN 100\n\n WHEN e.note_group = 'VP'\n AND e.note_type = 'DAR'\n THEN 95\n\n WHEN e.status_code = 800\n THEN 90\n\n WHEN e.status_code = 700\n THEN 80\n\n WHEN e.status_code = 550\n THEN 70\n\n WHEN e.note_group = 'LH'\n THEN 65\n\n WHEN e.status_code = 500\n THEN 60\n\n WHEN e.status_code = 300\n THEN 50\n\n WHEN e.note_group = 'JIN'\n AND e.note_type = 'IIN'\n AND (\n e.note_text ILIKE '%Verzonden%'\n OR e.note_text ILIKE '%Shipped%'\n )\n THEN 40\n\n WHEN e.note_group = 'POC'\n THEN 30\n\n WHEN e.note_type = 'COLAC'\n THEN 20\n\n WHEN e.note_type = 'COLRQ'\n THEN 10\n\n ELSE 0\n END\n\n FROM input i\n WHERE e.tracking_id = i.tracking_id\n\n RETURNING e.tracking_id\n),\n\nagg AS (\n SELECT\n e.tracking_id,\n\n MIN(e.event_at) FILTER (\n WHERE e.note_group = 'POC'\n AND e.note_type = 'POC'\n AND e.note_code_id = 219999\n ) AS collected_at,\n\n MIN(e.event_at) FILTER (\n WHERE e.note_group = 'JIN'\n AND e.note_type = 'IIN'\n AND e.note_code_id = 52001\n AND (\n e.note_text ILIKE '%Verzonden%'\n OR e.note_text ILIKE '%Shipped%'\n )\n ) AS manifested_at,\n\n MAX(\n COALESCE(\n CASE\n WHEN e.pod_date IS NOT NULL\n AND e.pod_time IS NOT NULL\n THEN e.pod_date::timestamp + e.pod_time\n ELSE NULL\n END,\n e.event_at\n )\n ) FILTER (\n WHERE e.status_code = 900\n AND e.note_group = 'POD'\n AND e.note_type = 'POD'\n AND e.note_code_id = 36\n ) AS pod_delivered_at,\n\n MAX(e.event_at) FILTER (\n WHERE e.interpreted_status = 'DELETED'\n ) AS deleted_at,\n\n MAX(e.event_at) AS last_event_at\n\n FROM palletways_status_events e\n JOIN input i\n ON i.tracking_id = e.tracking_id\n\n GROUP BY e.tracking_id\n),\n\nlatest_progress AS (\n SELECT DISTINCT ON (e.tracking_id)\n e.tracking_id,\n e.event_at,\n e.interpreted_status,\n e.note_text\n\n FROM palletways_status_events e\n JOIN input i\n ON i.tracking_id = e.tracking_id\n\n WHERE e.interpreted_status IS NOT NULL\n AND e.interpreted_status NOT IN (\n 'GENERAL INFORMATION',\n 'CUSTOMER SERVICE NOTE',\n 'POD IMAGE',\n 'UNKNOWN'\n )\n\n ORDER BY\n e.tracking_id,\n e.status_priority DESC,\n e.event_at DESC NULLS LAST,\n e.id DESC\n),\n\nlatest_note AS (\n SELECT DISTINCT ON (e.tracking_id)\n e.tracking_id,\n e.event_at,\n e.note_text\n\n FROM palletways_status_events e\n JOIN input i\n ON i.tracking_id = e.tracking_id\n\n ORDER BY\n e.tracking_id,\n e.event_at DESC NULLS LAST,\n e.id DESC\n),\n\nupdate_shipments AS (\nUPDATE palletways_shipments s\nSET\n is_collected =\n COALESCE(s.is_collected, false)\n OR agg.collected_at IS NOT NULL,\n\n collected_at =\n COALESCE(\n s.collected_at,\n agg.collected_at\n ),\n\n is_manifested =\n COALESCE(s.is_manifested, false)\n OR agg.manifested_at IS NOT NULL,\n\n manifested_at =\n COALESCE(\n s.manifested_at,\n agg.manifested_at\n ),\n\n is_deleted =\n COALESCE(s.is_deleted, false)\n OR agg.deleted_at IS NOT NULL,\n\n deleted_at =\n COALESCE(\n s.deleted_at,\n agg.deleted_at\n ),\n\n is_delivered =\n COALESCE(s.is_delivered, false)\n OR agg.pod_delivered_at IS NOT NULL,\n\n delivered_at =\n COALESCE(\n s.delivered_at,\n agg.pod_delivered_at\n ),\n\n last_note_status =\n latest_progress.interpreted_status,\n\n last_note_text =\n latest_note.note_text,\n\n last_note_at =\n latest_note.event_at,\n\n updated_at =\n now()\n\nFROM agg\n\nLEFT JOIN latest_progress\n ON latest_progress.tracking_id = agg.tracking_id\n\nLEFT JOIN latest_note\n ON latest_note.tracking_id = agg.tracking_id\n\nWHERE s.tracking_id = agg.tracking_id\nRETURNING s.tracking_id\n)\n\nSELECT\n i.tracking_id,\n i.con_no,\n i.notes_url,\n i.summary_url,\n COALESCE((SELECT COUNT(*) FROM events_deduplicated), 0)::int AS parsed_event_count,\n COALESCE((SELECT COUNT(*) FROM insert_events), 0)::int AS inserted_event_count,\n COALESCE((SELECT COUNT(*) FROM classify), 0)::int AS classified_event_count,\n COALESCE((SELECT COUNT(*) FROM update_shipments), 0)::int AS shipment_note_update_count\nFROM input i;", "options": {} }, - "id": "1b860a8c-8191-49b7-a2c1-3e5f174c7ed3", - "name": "Postgres - Insert status event", + "id": "c0c3732f-3250-4bf3-ba2a-30b5ac21aaaf", + "name": "Postgres - Upsert notes and note fields", "type": "n8n-nodes-base.postgres", "typeVersion": 2.6, "position": [ - 224, - 560 + 2624, + 1648 ], "credentials": { "postgres": { @@ -136,19 +144,53 @@ } } }, + { + "parameters": { + "url": "={{ $json.summary_url }}", + "options": { + "response": { + "response": { + "responseFormat": "text" + } + }, + "timeout": 120000 + } + }, + "id": "6cff9996-5235-4d8a-af5c-805c5e8bcd84", + "name": "HTTP - Get summary status", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.2, + "position": [ + 2864, + 1648 + ] + }, + { + "parameters": { + "jsCode": "const shipment = $('Postgres - Upsert notes and note fields').item.json;\n\nfunction getXmlFromJson(j) {\n if (typeof j === 'string') return j;\n if (typeof j.data === 'string') return j.data;\n if (typeof j.body === 'string') return j.body;\n if (typeof j.response === 'string') return j.response;\n\n for (const v of Object.values(j)) {\n if (typeof v === 'string' && v.trim().startsWith('<')) return v;\n }\n\n return JSON.stringify(j);\n}\n\nfunction xmlUnescape(s) {\n if (s === null || s === undefined) return null;\n return String(s)\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/&/g, '&')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\");\n}\n\nfunction tag(block, name) {\n const re = new RegExp(`<${name}>([\\\\s\\\\S]*?)<\\\\/${name}>`, 'i');\n const m = String(block || '').match(re);\n return m ? xmlUnescape(m[1]).trim() : null;\n}\n\nfunction dataBlocks(xml) {\n const blocks = [];\n const re = /([\\s\\S]*?)<\\/Data>/gi;\n let m;\n while ((m = re.exec(String(xml || ''))) !== null) {\n blocks.push(m[1]);\n }\n return blocks;\n}\n\nconst xml = getXmlFromJson($json);\nconst block = dataBlocks(xml)[0] || '';\n\nconst summary = {\n tracking_id: tag(block, 'TrackingID') || shipment.tracking_id,\n con_no: tag(block, 'ConNo') || shipment.con_no,\n date_entered: tag(block, 'DateEntered'),\n cust_ref: tag(block, 'CustRef'),\n lifts: tag(block, 'Lifts'),\n weight: tag(block, 'Weight'),\n status: tag(block, 'Status'),\n barcodes: tag(block, 'Barcodes'),\n col_depot: tag(block, 'ColDepot'),\n del_depot: tag(block, 'DelDepot'),\n};\n\nreturn [{\n json: {\n ...shipment,\n summary_status: summary.status ? String(summary.status).trim() : null,\n summary_raw: summary,\n }\n}];" + }, + "id": "97d97299-d6b9-449a-bb3e-89937836c9e9", + "name": "Code - Parse summary status", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 3088, + 1648 + ] + }, { "parameters": { "operation": "executeQuery", - "query": "WITH agg AS (\n SELECT\n e.tracking_id,\n\n MIN(e.event_at) FILTER (\n WHERE e.note_group = 'POC'\n AND e.note_type = 'POC'\n AND e.note_code_id = 219999\n ) AS collected_at,\n\n MIN(e.event_at) FILTER (\n WHERE e.note_group = 'JIN'\n AND e.note_type = 'IIN'\n AND e.note_code_id = 52001\n AND (\n e.note_text ILIKE '%Verzonden%'\n OR e.note_text ILIKE '%Shipped%'\n )\n ) AS manifested_at,\n\n MAX(COALESCE(\n CASE\n WHEN e.pod_date IS NOT NULL AND e.pod_time IS NOT NULL\n THEN e.pod_date::timestamp + e.pod_time\n ELSE NULL\n END,\n e.event_at\n )) FILTER (\n WHERE e.status_code = 900\n AND e.note_group = 'POD'\n AND e.note_type = 'POD'\n AND e.note_code_id = 36\n ) AS delivered_at,\n\n MAX(e.event_at) AS last_event_at\n FROM palletways_status_events e\n WHERE e.tracking_id = '{{ $(\"Code - Build Palletways URL\").item.json.tracking_id }}'\n GROUP BY e.tracking_id\n),\nlatest AS (\n SELECT DISTINCT ON (e.tracking_id)\n e.tracking_id,\n e.event_at,\n e.interpreted_status AS derived_status,\n e.status_phase,\n e.status_priority\n FROM palletways_status_events e\n WHERE e.tracking_id = '{{ $(\"Code - Build Palletways URL\").item.json.tracking_id }}'\n AND e.interpreted_status IS NOT NULL\n AND e.interpreted_status NOT IN (\n 'GENERAL INFORMATION',\n 'CUSTOMER SERVICE NOTE',\n 'POD IMAGE',\n 'UNKNOWN'\n )\n ORDER BY\n e.tracking_id,\n e.status_priority DESC,\n e.event_at DESC NULLS LAST,\n e.id DESC\n)\nUPDATE palletways_shipments s\nSET\n is_collected = COALESCE(s.is_collected, false) OR agg.collected_at IS NOT NULL,\n collected_at = COALESCE(s.collected_at, agg.collected_at),\n\n is_manifested = COALESCE(s.is_manifested, false) OR agg.manifested_at IS NOT NULL,\n manifested_at = COALESCE(s.manifested_at, agg.manifested_at),\n\n is_delivered = COALESCE(s.is_delivered, false) OR agg.delivered_at IS NOT NULL,\n delivered_at = COALESCE(s.delivered_at, agg.delivered_at),\n\n current_status = CASE\n WHEN agg.delivered_at IS NOT NULL THEN 'DELIVERED'\n ELSE COALESCE(latest.derived_status, s.current_status, 'NO EVENTS')\n END,\n\n current_status_at = COALESCE(\n agg.delivered_at,\n latest.event_at,\n agg.last_event_at,\n s.current_status_at\n ),\n\n last_status_poll_at = now(),\n\n next_status_poll_at = CASE\n WHEN s.is_collection_role = true\n AND COALESCE(s.is_delivery_role, false) = false\n AND agg.manifested_at IS NOT NULL\n THEN NULL\n\n WHEN agg.delivered_at IS NOT NULL\n THEN NULL\n\n ELSE now() + interval '2 hours'\n END,\n\n status_poll_done = CASE\n WHEN s.is_collection_role = true\n AND COALESCE(s.is_delivery_role, false) = false\n AND agg.manifested_at IS NOT NULL\n THEN true\n\n WHEN agg.delivered_at IS NOT NULL\n THEN true\n\n ELSE false\n END,\n\n status_source = 'palletways_notes_api',\n updated_at = now()\nFROM agg\nLEFT JOIN latest ON latest.tracking_id = agg.tracking_id\nWHERE s.tracking_id = agg.tracking_id\nAND s.tracking_id = '{{ $(\"Code - Build Palletways URL\").item.json.tracking_id }}';", + "query": "WITH input AS (\n SELECT\n '{{ $json.tracking_id }}'::text AS tracking_id,\n NULLIF('{{ ($json.summary_status ?? \"\").toString().replace(/'/g, \"''\") }}', '')::text AS summary_status,\n '{{ JSON.stringify($json.summary_raw ?? {}).replace(/'/g, \"''\") }}'::jsonb AS summary_raw\n)\nUPDATE palletways_shipments s\nSET\n con_no = COALESCE(s.con_no, input.summary_raw->>'con_no'),\n\n summary_status = COALESCE(input.summary_status, s.summary_status),\n summary_status_at = CASE\n WHEN input.summary_status IS NOT NULL THEN now()\n ELSE s.summary_status_at\n END,\n\n current_status = CASE\n WHEN input.summary_status IS NOT NULL THEN input.summary_status\n ELSE COALESCE(s.current_status, s.last_note_status, 'NO STATUS')\n END,\n\n current_status_at = CASE\n WHEN input.summary_status IS NOT NULL THEN now()\n ELSE COALESCE(s.current_status_at, s.last_note_at)\n END,\n\n is_deleted = CASE\n WHEN upper(COALESCE(input.summary_status, '')) IN (\n 'DELETED',\n 'DELETE',\n 'JOB DELETED',\n 'CANCELLED',\n 'CANCELED'\n )\n THEN true\n ELSE COALESCE(s.is_deleted, false)\n END,\n\n deleted_at = CASE\n WHEN upper(COALESCE(input.summary_status, '')) IN (\n 'DELETED',\n 'DELETE',\n 'JOB DELETED',\n 'CANCELLED',\n 'CANCELED'\n )\n AND s.deleted_at IS NULL\n THEN now()\n ELSE s.deleted_at\n END,\n\n is_never_arrived = CASE\n WHEN upper(COALESCE(input.summary_status, '')) IN (\n 'NEVER ARRIVED',\n 'NEVERARRIVED'\n )\n THEN true\n ELSE COALESCE(s.is_never_arrived, false)\n END,\n\n never_arrived_at = CASE\n WHEN upper(COALESCE(input.summary_status, '')) IN (\n 'NEVER ARRIVED',\n 'NEVERARRIVED'\n )\n AND s.never_arrived_at IS NULL\n THEN now()\n ELSE s.never_arrived_at\n END,\n\n is_delivered = CASE\n WHEN upper(COALESCE(input.summary_status, '')) IN (\n 'JOB COMPLETE',\n 'DELIVERED'\n )\n THEN true\n WHEN s.delivered_at IS NOT NULL THEN true\n ELSE COALESCE(s.is_delivered, false)\n END,\n\n status_poll_done = CASE\n WHEN upper(COALESCE(input.summary_status, '')) IN (\n 'DELETED',\n 'DELETE',\n 'JOB DELETED',\n 'CANCELLED',\n 'CANCELED',\n 'NEVER ARRIVED',\n 'NEVERARRIVED',\n 'JOB COMPLETE',\n 'DELIVERED'\n )\n THEN true\n\n WHEN s.is_collection_role = true\n AND COALESCE(s.is_delivery_role, false) = false\n AND COALESCE(s.is_manifested, false) = true\n THEN true\n\n ELSE false\n END,\n\n next_status_poll_at = CASE\n WHEN upper(COALESCE(input.summary_status, '')) IN (\n 'DELETED',\n 'DELETE',\n 'JOB DELETED',\n 'CANCELLED',\n 'CANCELED',\n 'NEVER ARRIVED',\n 'NEVERARRIVED',\n 'JOB COMPLETE',\n 'DELIVERED'\n )\n THEN NULL\n\n WHEN s.is_collection_role = true\n AND COALESCE(s.is_delivery_role, false) = false\n AND COALESCE(s.is_manifested, false) = true\n THEN NULL\n\n ELSE now() + interval '2 hours'\n END,\n\n last_status_poll_at = now(),\n status_source = CASE\n WHEN input.summary_status IS NOT NULL THEN 'palletways_summary_api'\n ELSE COALESCE(s.status_source, 'palletways_notes_api')\n END,\n updated_at = now()\nFROM input\nWHERE s.tracking_id = input.tracking_id\nRETURNING\n s.tracking_id,\n s.current_status,\n s.summary_status,\n s.status_poll_done,\n s.next_status_poll_at,\n s.is_delivered,\n s.is_deleted,\n s.is_never_arrived;", "options": {} }, - "id": "ec1a02cf-8dc0-4529-8b15-d6db60fd39d9", - "name": "Postgres - Recalculate shipment state", + "id": "0b6a0c25-b87a-429a-98c6-6b341f5ed573", + "name": "Postgres - Update official current status", "type": "n8n-nodes-base.postgres", "typeVersion": 2.6, "position": [ - 576, - 560 + 3328, + 1648 ], "credentials": { "postgres": { @@ -159,40 +201,30 @@ }, { "parameters": {}, - "id": "a23f3a53-d305-4ea0-a9da-1b8edaabbd24", + "id": "e7012c24-624f-42d1-a1fc-6b7dee63b7b4", "name": "Done", "type": "n8n-nodes-base.noOp", "typeVersion": 1, "position": [ - -656, - 832 + 1936, + 1968 ] - }, - { - "parameters": { - "operation": "executeQuery", - "query": "UPDATE palletways_status_events\nSET\n interpreted_status = CASE\n WHEN note_group = 'JIN' AND note_type = 'COLRQ' AND note_code_id = 353999 THEN 'COLLECTION REQUESTED'\n WHEN note_group = 'JIN' AND note_type = 'COLAC' AND note_code_id = 352999 THEN 'COLLECTION ACCEPTED'\n WHEN note_group = 'POC' AND note_type = 'POC' AND note_code_id = 219999 THEN 'COLLECTED'\n WHEN note_group = 'JIN' AND note_type = 'IIN' AND note_code_id = 52001 AND (note_text ILIKE '%Verzonden%' OR note_text ILIKE '%Shipped%') THEN 'MANIFESTED'\n WHEN status_code = 300 AND note_group = 'VL' AND note_type = 'SCN' AND note_code_id = 48001 THEN 'LOADED COLLECTION VEHICLE'\n WHEN status_code = 500 AND note_group = 'VU' AND note_type = 'SCN' AND note_code_id = 49001 THEN 'UNLOADED AT HUB'\n WHEN status_code = 500 AND note_group = 'BAY' AND note_type = 'SCN' AND note_code_id = 47001 THEN 'SCANNED IN BAY'\n WHEN note_group = 'LH' AND note_type = 'LLD' AND note_code_id = 101999 THEN 'LINEHAUL LOAD'\n WHEN note_group = 'LH' AND note_type = 'LUL' AND note_code_id = 102999 THEN 'LINEHAUL UNLOAD'\n WHEN note_group = 'LH' AND note_type = 'LBA' AND note_code_id = 103999 THEN 'LINEHAUL BAYCHECK'\n WHEN status_code = 550 AND note_group = 'JIN' AND note_type = 'GDEP' AND note_code_id = 348999 THEN 'LEFT HUB'\n WHEN status_code = 700 AND note_group = 'VU' AND note_type = 'SCN' AND note_code_id = 49001 THEN 'AT DELIVERY DEPOT'\n WHEN status_code = 800 AND note_group = 'VL' AND note_type = 'SCN' AND note_code_id = 48001 THEN 'OUT FOR DELIVERY'\n WHEN note_group = 'VP' AND note_type = 'PDT' AND note_code_id = 218999 THEN 'ETA PLANNED'\n WHEN note_group = 'VP' AND note_type = 'DAR' AND note_code_id = 307999 THEN 'DRIVER ARRIVED'\n WHEN status_code = 900 AND note_group = 'POD' AND note_type = 'POD' AND note_code_id = 36 THEN 'DELIVERED'\n WHEN note_group = 'POD' AND note_type = 'IMG' AND note_code_id = 67999 THEN 'POD IMAGE'\n WHEN note_group = 'BIN' AND note_type = 'BIN' AND note_code_id = 32 THEN 'BOOKED IN'\n WHEN note_group = 'CUSV' AND note_type = 'CUSN' AND note_code_id = 297999 THEN 'CUSTOMER SERVICE NOTE'\n WHEN note_group = 'JIN' AND note_type IN ('IIN', 'GIN') THEN 'GENERAL INFORMATION'\n ELSE 'UNKNOWN'\n END,\n\n status_phase = CASE\n WHEN note_group = 'JIN' AND note_type IN ('COLRQ', 'COLAC') THEN 'COLLECTION_PLANNING'\n WHEN note_group = 'POC' AND note_type = 'POC' THEN 'COLLECTED'\n WHEN note_group = 'JIN' AND note_type = 'IIN' AND note_code_id = 52001 AND (note_text ILIKE '%Verzonden%' OR note_text ILIKE '%Shipped%') THEN 'MANIFESTED'\n WHEN status_code IN (300, 500, 525, 530, 550) OR note_group IN ('LH', 'BAY') THEN 'IN_NETWORK'\n WHEN status_code = 700 THEN 'DELIVERY_DEPOT'\n WHEN status_code = 800 OR note_group = 'VP' THEN 'DELIVERY_ROUTE'\n WHEN status_code = 900 AND note_group = 'POD' THEN 'DELIVERED'\n WHEN note_group = 'BIN' THEN 'BOOKING'\n ELSE 'INFO'\n END,\n\n status_priority = CASE\n WHEN status_code = 900 AND note_group = 'POD' THEN 100\n WHEN note_group = 'VP' AND note_type = 'DAR' THEN 95\n WHEN status_code = 800 THEN 90\n WHEN status_code = 700 THEN 80\n WHEN status_code = 550 THEN 70\n WHEN note_group = 'LH' THEN 65\n WHEN status_code = 500 THEN 60\n WHEN status_code = 300 THEN 50\n WHEN note_group = 'JIN' AND note_type = 'IIN' AND (note_text ILIKE '%Verzonden%' OR note_text ILIKE '%Shipped%') THEN 40\n WHEN note_group = 'POC' THEN 30\n WHEN note_type = 'COLAC' THEN 20\n WHEN note_type = 'COLRQ' THEN 10\n ELSE 0\n END\nWHERE tracking_id = '{{ $(\"Code - Build Palletways URL\").item.json.tracking_id }}';", - "options": {} - }, - "type": "n8n-nodes-base.postgres", - "typeVersion": 2.6, - "position": [ - 432, - 560 - ], - "id": "2a3b5067-4cd5-450d-8f8c-abf2263aba0e", - "name": "Execute a SQL query", - "credentials": { - "postgres": { - "id": "a0jnANQ89VLSby0b", - "name": "Palletways DB" - } - } } ], "pinData": {}, "connections": { "Schedule Trigger - elke 2 uur": { + "main": [ + [ + { + "node": "Postgres - Setup columns and indexes", + "type": "main", + "index": 0 + } + ] + ] + }, + "Postgres - Setup columns and indexes": { "main": [ [ { @@ -225,69 +257,80 @@ ], [ { - "node": "Code - Build Palletways URL", + "node": "Code - Build URLs", "type": "main", "index": 0 } ] ] }, - "Code - Build Palletways URL": { + "Code - Build URLs": { "main": [ [ { - "node": "HTTP - Get Palletways notes", + "node": "HTTP - Get detailed notes", "type": "main", "index": 0 } ] ] }, - "HTTP - Get Palletways notes": { + "HTTP - Get detailed notes": { "main": [ [ { - "node": "XML - Parse notes response", + "node": "Code - Parse notes", "type": "main", "index": 0 } ] ] }, - "XML - Parse notes response": { + "Code - Parse notes": { "main": [ [ { - "node": "Code - Normalize status events", + "node": "Postgres - Upsert notes and note fields", "type": "main", "index": 0 } ] ] }, - "Code - Normalize status events": { + "Postgres - Upsert notes and note fields": { "main": [ [ { - "node": "Postgres - Insert status event", + "node": "HTTP - Get summary status", "type": "main", "index": 0 } ] ] }, - "Postgres - Insert status event": { + "HTTP - Get summary status": { "main": [ [ { - "node": "Execute a SQL query", + "node": "Code - Parse summary status", "type": "main", "index": 0 } ] ] }, - "Postgres - Recalculate shipment state": { + "Code - Parse summary status": { + "main": [ + [ + { + "node": "Postgres - Update official current status", + "type": "main", + "index": 0 + } + ] + ] + }, + "Postgres - Update official current status": { "main": [ [ { @@ -297,17 +340,6 @@ } ] ] - }, - "Execute a SQL query": { - "main": [ - [ - { - "node": "Postgres - Recalculate shipment state", - "type": "main", - "index": 0 - } - ] - ] } }, "active": true, @@ -318,10 +350,9 @@ "callerPolicy": "workflowsFromSameOwner", "availableInMCP": false, "timeSavedMode": "fixed", - "errorWorkflow": "1Ps5lukDf2sgcGL7", - "saveDataSuccessExecution": "all" + "errorWorkflow": "1Ps5lukDf2sgcGL7" }, - "versionId": "7dc4122f-1471-417b-bbcb-6a07cf68b1e1", + "versionId": "5bf447c5-6168-4ddd-a47d-43f7df55bdb0", "meta": { "templateCredsSetupCompleted": true, "instanceId": "bef8d409866a58c0777dfe7cca1b9c2400fd051c056d361501393ab423006b5f"