Upload files to "palletways"
Toevoegen van Collections aan de Palletways database
This commit is contained in:
commit
e14003d64b
1 file changed
+420
@@ -0,0 +1,420 @@
|
||||
{
|
||||
"name": "Palletways BI Collections",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [
|
||||
{
|
||||
"field": "minutes"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "595a3d6f-600d-4570-9969-d091b3edf125",
|
||||
"name": "Schedule Trigger",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"typeVersion": 1.1,
|
||||
"position": [
|
||||
-7728,
|
||||
-736
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {
|
||||
"explicitArray": false,
|
||||
"mergeAttrs": true
|
||||
}
|
||||
},
|
||||
"id": "f981fd53-c214-4d4b-9890-cc0ebe68eb4f",
|
||||
"name": "Parse XML Response",
|
||||
"type": "n8n-nodes-base.xml",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
-6832,
|
||||
-640
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "e1fc6f17-b059-4b78-a365-c2428d3874f6",
|
||||
"name": "Process Each Consignment",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
-6160,
|
||||
-656
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "=https://api.palletways.com/getconsignment/{{ $json.trackingid }}?apikey=SXJaM7PLjZDqfsnXSDP8Y9wDY6crxJySBg705MQEPus%3D",
|
||||
"options": {
|
||||
"response": {
|
||||
"response": {
|
||||
"responseFormat": "text"
|
||||
}
|
||||
},
|
||||
"timeout": 10000
|
||||
}
|
||||
},
|
||||
"id": "74c9bff6-64f0-4f7b-bda8-09c2d180bf8e",
|
||||
"name": "Get Consignment Details",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.1,
|
||||
"position": [
|
||||
-5920,
|
||||
-640
|
||||
],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Fixed timestamp parser - handles Redis data under \"propertyName\"\n\n// Helper function to format timestamps in UK timezone\nfunction formatUKTimestamp(date) {\n const options = {\n timeZone: 'Europe/London',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false\n };\n \n const formatter = new Intl.DateTimeFormat('en-CA', options);\n const parts = formatter.formatToParts(date);\n \n const year = parts.find(p => p.type === 'year').value;\n const month = parts.find(p => p.type === 'month').value;\n const day = parts.find(p => p.type === 'day').value;\n const hour = parts.find(p => p.type === 'hour').value;\n const minute = parts.find(p => p.type === 'minute').value;\n const second = parts.find(p => p.type === 'second').value;\n \n return {\n timestamp: `${year}-${month}-${day}T${hour}:${minute}:${second}`,\n date: `${year}-${month}-${day}`,\n time: `${hour}:${minute}:${second}`\n };\n}\n\n// Generate current UK timestamp (this will be saved for the NEXT run)\nconst now = new Date();\nconst currentUK = formatUKTimestamp(now);\n\n// Initialize variables for the PREVIOUS run timestamp\nlet lastRunTimestamp, lastRunDate, lastRunTime;\nlet source = 'unknown';\n\ntry {\n // Get the Redis response\n const input = $input.first()?.json;\n console.log('Full input received:', JSON.stringify(input, null, 2));\n \n // The Redis data is coming in under \"propertyName\" as a JSON string\n let parsedData = null;\n \n if (input?.propertyName) {\n // Parse the JSON string from propertyName\n parsedData = JSON.parse(input.propertyName);\n source = 'input.propertyName';\n console.log('Found and parsed Redis data from propertyName');\n } else if (input?.data) {\n // Fallback: check data property\n if (typeof input.data === 'string') {\n parsedData = JSON.parse(input.data);\n source = 'input.data-parsed';\n } else {\n parsedData = input.data;\n source = 'input.data-object';\n }\n console.log('Found Redis data in input.data');\n } else if (input?.lastRunTimestamp) {\n // Direct object\n parsedData = input;\n source = 'direct-input';\n console.log('Found Redis data directly in input');\n }\n \n console.log('Parsed Redis data:', JSON.stringify(parsedData, null, 2));\n \n if (!parsedData) {\n throw new Error('No Redis data found');\n }\n \n // Extract the previous timestamp - use exactly as stored in Redis\n if (parsedData.lastRunTimestamp) {\n lastRunTimestamp = parsedData.lastRunTimestamp; // Use exact value from Redis\n lastRunDate = parsedData.lastRunDate; // Use exact value from Redis\n lastRunTime = parsedData.lastRunTime; // Use exact value from Redis\n \n console.log(`✅ Successfully loaded PREVIOUS timestamp from Redis:`);\n console.log(` Timestamp: ${lastRunTimestamp}`);\n console.log(` Date: ${lastRunDate}`);\n console.log(` Time: ${lastRunTime}`);\n source = 'redis-success';\n \n } else {\n throw new Error('No lastRunTimestamp found in Redis data');\n }\n \n} catch (error) {\n // Fallback: Use 24 hours ago as the \"previous\" run time\n console.log(`❌ Error loading from Redis: ${error.message}`);\n console.log('Using 24-hour fallback for first run');\n \n const yesterday = new Date();\n yesterday.setHours(yesterday.getHours() - 24);\n const fallbackUK = formatUKTimestamp(yesterday);\n \n lastRunTimestamp = fallbackUK.timestamp;\n lastRunDate = fallbackUK.date;\n lastRunTime = fallbackUK.time;\n source = 'fallback-24h';\n \n console.log(`📅 Using fallback timestamp:`);\n console.log(` Timestamp: ${lastRunTimestamp}`);\n console.log(` Date: ${lastRunDate}`);\n console.log(` Time: ${lastRunTime}`);\n}\n\n// Log the final results\nconsole.log('\\n🔄 FINAL RESULTS:');\nconsole.log(`📍 PREVIOUS run (to use for queries): ${lastRunTimestamp}`);\nconsole.log(`🕐 CURRENT run (to save for next time): ${currentUK.timestamp}`);\nconsole.log(`📊 Data source: ${source}`);\n\n// Return both timestamps with clear separation\nreturn [{\n json: {\n // PREVIOUS run timestamps (use these for your data queries)\n lastRunTimestamp: lastRunTimestamp,\n lastRunDate: lastRunDate,\n lastRunTime: lastRunTime,\n \n // CURRENT run timestamps (save these to Redis for next run)\n currentTimestamp: currentUK.timestamp,\n currentDate: currentUK.date,\n currentTime: currentUK.time,\n \n // Metadata\n workflowId: $workflow.id,\n executionId: $execution.id,\n dataSource: source,\n processedAt: new Date().toISOString()\n }\n}];"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
-7280,
|
||||
-736
|
||||
],
|
||||
"id": "7318a9b6-f3b7-4422-ad54-68837318c243",
|
||||
"name": "Parse Last Timestamp"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "get",
|
||||
"key": "collection_lastrun_timestamp",
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.redis",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
-7504,
|
||||
-736
|
||||
],
|
||||
"id": "4f2df54f-0ec5-4b87-b777-a5a0821e8adc",
|
||||
"name": "Get Last Timestamp from Redis",
|
||||
"credentials": {
|
||||
"redis": {
|
||||
"id": "uPJ4dFmf6tm25qHY",
|
||||
"name": "Redis account 2"
|
||||
}
|
||||
},
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "set",
|
||||
"key": "collection_lastrun_timestamp",
|
||||
"value": "={{ $json.redisValue }}"
|
||||
},
|
||||
"type": "n8n-nodes-base.redis",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
-5440,
|
||||
-848
|
||||
],
|
||||
"id": "ee8ec58b-9840-437c-b481-1f6ca200ff7a",
|
||||
"name": "Save Timestamp to Redis",
|
||||
"credentials": {
|
||||
"redis": {
|
||||
"id": "uPJ4dFmf6tm25qHY",
|
||||
"name": "Redis account 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Sla de CURRENT execution time pas op NADAT de collections succesvol zijn verwerkt.\n// De waarden komen uit de node \"Parse Last Timestamp\".\n\nconst input = $items('Parse Last Timestamp')[0]?.json;\n\nif (!input) {\n throw new Error('Kon data uit node \"Parse Last Timestamp\" niet vinden.');\n}\n\nconst timestampData = {\n lastRunTimestamp: input.currentTimestamp,\n lastRunDate: input.currentDate,\n lastRunTime: input.currentTime,\n workflowId: input.workflowId,\n executionId: input.executionId,\n savedAt: new Date().toISOString(),\n version: '3.2'\n};\n\nreturn [{\n json: {\n success: true,\n timestampData,\n redisValue: JSON.stringify(timestampData),\n summary: {\n whatWeUsedThisRun: {\n lastRunTimestamp: input.lastRunTimestamp,\n source: input.dataSource\n },\n whatWeSaveForNextRun: {\n lastRunTimestamp: timestampData.lastRunTimestamp,\n note: 'Timestamp is opgeslagen na succesvolle verwerking of na een controle zonder nieuwe collection zendingen.'\n }\n }\n }\n}];"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
-5680,
|
||||
-848
|
||||
],
|
||||
"id": "aa388c2e-22fd-4895-be5d-618b925c7e94",
|
||||
"name": "Prepare Timestamp Data"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "=https://api.palletways.com/conColDepot/{{$now.setZone('Europe/Amsterdam').minus({ days: 3 }).toFormat('yyyy-MM-dd')}}/{{$now.setZone('Europe/Amsterdam').toFormat('yyyy-MM-dd')}}?apikey=SXJaM7PLjZDqfsnXSDP8Y9wDY6crxJySBg705MQEPus%3D",
|
||||
"options": {
|
||||
"response": {
|
||||
"response": {
|
||||
"responseFormat": "text"
|
||||
}
|
||||
},
|
||||
"timeout": 30000
|
||||
}
|
||||
},
|
||||
"id": "3dc98464-f24e-4291-bca8-2f425a192bb8",
|
||||
"name": "Get Collections",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.1,
|
||||
"position": [
|
||||
-7056,
|
||||
-640
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const OUR_DEPOT = 464;\nconst out = [];\n\nconst asArray = (v) => {\n if (v === undefined || v === null || v === '') return [];\n return Array.isArray(v) ? v : [v];\n};\n\nconst text = (v) => {\n if (v === undefined || v === null) return null;\n const s = String(v).trim();\n return s === '' ? null : s;\n};\n\nconst num = (v) => {\n const s = text(v);\n if (s === null) return null;\n const n = Number(String(s).replace(',', '.'));\n return Number.isFinite(n) ? n : null;\n};\n\nconst intVal = (v) => {\n const n = num(v);\n return n === null ? null : Math.trunc(n);\n};\n\nconst yesNoBool = (v) => {\n const s = String(v ?? '').trim().toLowerCase();\n if (['yes', 'true', '1', 'ja', 'y', 'on'].includes(s)) return true;\n if (['no', 'false', '0', 'nee', 'n', 'off'].includes(s)) return false;\n return null;\n};\n\nconst normalizeCountry = (v) => {\n const s = text(v);\n if (!s) return null;\n const u = s.toUpperCase();\n return u === 'UK' ? 'GB' : u;\n};\n\nconst decodeXml = (s) => String(s ?? '')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\");\n\nconst tag = (xml, name) => {\n if (!xml) return null;\n const re = new RegExp(`<${name}(?:\\\\s[^>]*)?>([\\\\s\\\\S]*?)<\\\\/${name}>`, 'i');\n const m = String(xml).match(re);\n return m ? text(decodeXml(m[1])) : null;\n};\n\nconst blocks = (xml, name) => {\n if (!xml) return [];\n const re = new RegExp(`<${name}(?:\\\\s[^>]*)?>([\\\\s\\\\S]*?)<\\\\/${name}>`, 'gi');\n const result = [];\n let m;\n while ((m = re.exec(String(xml))) !== null) {\n result.push(m[1]);\n }\n return result;\n};\n\nconst firstBlock = (xml, name) => blocks(xml, name)[0] || '';\n\nconst safeNodeItem = (name) => {\n try {\n return $(name).item.json || {};\n } catch (e) {\n try {\n return $items(name)?.[0]?.json || {};\n } catch (e2) {\n return {};\n }\n }\n};\n\nconst makeDateTime = (dateValue, timeValue) => {\n const d = text(dateValue);\n const t = text(timeValue);\n if (!d) return null;\n\n // Already datetime\n if (d.includes('T') || /^\\d{4}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}/.test(d)) {\n return d.replace(' ', 'T');\n }\n\n return `${d}T${t || '00:00:00'}`;\n};\n\nconst dateOnly = (v) => {\n const s = text(v);\n if (!s) return null;\n return s.substring(0, 10);\n};\n\nconst statusFlags = (statusValue, statusDateTime) => {\n const status = text(statusValue);\n const s = String(status || '').toLowerCase();\n\n const isDelivered =\n s.includes('delivered') ||\n s.includes('afgeleverd') ||\n s.includes('pod');\n\n const isCollected =\n s.includes('collected') ||\n s.includes('picked up') ||\n s.includes('pickup complete') ||\n s.includes('collection complete') ||\n s.includes('opgehaald') ||\n s.includes('afgehaald');\n\n const isDeleted =\n s.includes('deleted') ||\n s.includes('removed') ||\n s.includes('cancelled') ||\n s.includes('canceled') ||\n s.includes('verwijderd') ||\n s.includes('geannuleerd');\n\n return {\n current_status: status,\n current_status_at: status ? statusDateTime : null,\n is_delivered: isDelivered,\n delivered_at: isDelivered ? statusDateTime : null,\n is_collected: isCollected,\n collected_at: isCollected ? statusDateTime : null,\n is_deleted: isDeleted,\n deleted_at: isDeleted ? statusDateTime : null,\n status_source: status ? 'PALLETWAYS_API' : null,\n };\n};\n\nfunction addressFromXml(consignmentXml, type) {\n const addressBlocks = blocks(consignmentXml, 'Address');\n const wanted = addressBlocks.find(a => String(tag(a, 'Type') || '').toLowerCase() === type.toLowerCase()) || '';\n return {\n contact: tag(wanted, 'ContactName'),\n company: tag(wanted, 'CompanyName'),\n addr1: tag(wanted, 'Addr1'),\n addr2: tag(wanted, 'Addr2'),\n addr3: tag(wanted, 'Addr3'),\n addr4: tag(wanted, 'Addr4'),\n town: tag(wanted, 'Town'),\n county: tag(wanted, 'County'),\n country: normalizeCountry(tag(wanted, 'Country')),\n postcode: tag(wanted, 'PostCode'),\n };\n}\n\nfunction getAddressFromJson(consignment, type) {\n const addresses = asArray(consignment.Address);\n return addresses.find(a => String(a.Type || '').toLowerCase() === type.toLowerCase()) || {};\n}\n\nfunction getServiceFromXml(consignmentXml, type) {\n const serviceBlocks = blocks(consignmentXml, 'Service');\n return serviceBlocks.find(s => String(tag(s, 'Type') || '').toLowerCase() === type.toLowerCase()) || '';\n}\n\nfunction getServiceFromJson(consignment, type) {\n const services = asArray(consignment.Service);\n return services.find(s => String(s?.Type || '').toLowerCase() === type.toLowerCase()) || {};\n}\n\nfunction countPalletCodesXml(consignmentXml) {\n return blocks(consignmentXml, 'Pallet')\n .map(p => text(decodeXml(p)))\n .filter(Boolean).length;\n}\n\nfunction countPalletCodesJson(palletValue) {\n return asArrLine truncated
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
-5680,
|
||||
-640
|
||||
],
|
||||
"id": "a15754c5-a5e3-4448-98e9-adcf55074ae8",
|
||||
"name": "Normalize Palletways Consignment"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "executeQuery",
|
||||
"query": "insert into palletways_shipments (\n tracking_id,\n consignment_number,\n reference,\n collection_reference,\n manifest_date,\n manifest_time,\n paying_depot,\n collection_depot,\n delivery_depot,\n is_our_paying_depot,\n is_collection_by_us,\n is_delivery_by_us,\n is_manifested,\n account_name,\n account_code,\n consignment_type,\n classification,\n service_type,\n service_code,\n service_surcharge,\n collection_service_code,\n collection_service_surcharge,\n delivery_service_code,\n delivery_service_surcharge,\n due_date,\n due_time,\n planned_collection_date,\n planned_delivery_date,\n delivery_list_date,\n collection_list_date,\n manifested_at,\n lifts,\n weight_kg,\n bill_unit_type,\n bill_unit_amount,\n pallet_count,\n handball,\n tail_lift,\n adr_goods,\n limited_quantity_goods,\n booked_in,\n book_in_request,\n collection_company,\n collection_contact,\n collection_addr1,\n collection_addr2,\n collection_addr3,\n collection_addr4,\n collection_town,\n collection_county,\n collection_country,\n collection_postcode,\n delivery_company,\n delivery_contact,\n delivery_addr1,\n delivery_addr2,\n delivery_addr3,\n delivery_addr4,\n delivery_town,\n delivery_county,\n delivery_country,\n delivery_postcode,\n hub_sequence,\n hub_name,\n current_status,\n current_status_at,\n is_delivered,\n delivered_at,\n is_deleted,\n deleted_at,\n is_collected,\n collected_at,\n status_source,\n source_first_seen,\n source_last_seen,\n first_seen_in_bi_at,\n last_api_seen_at,\n raw_json,\n last_seen_at\n)\nvalues (\n '{{ ($json.tracking_id ?? \"\").replaceAll(\"'\", \"''\") }}',\n '{{ ($json.consignment_number ?? \"\").replaceAll(\"'\", \"''\") }}',\n '{{ ($json.reference ?? \"\").replaceAll(\"'\", \"''\") }}',\n '{{ ($json.collection_reference ?? \"\").replaceAll(\"'\", \"''\") }}',\n\n {{ $json.manifest_date ? \"'\" + $json.manifest_date + \"'\" : \"null\" }},\n {{ $json.manifest_time ? \"'\" + $json.manifest_time + \"'\" : \"null\" }},\n\n {{ $json.paying_depot ?? \"null\" }},\n {{ $json.collection_depot ?? \"null\" }},\n {{ $json.delivery_depot ?? \"null\" }},\n\n {{ $json.is_our_paying_depot ? \"true\" : \"false\" }},\n {{ $json.is_collection_by_us ? \"true\" : \"false\" }},\n {{ $json.is_delivery_by_us ? \"true\" : \"false\" }},\n {{ $json.is_manifested ? \"true\" : \"false\" }},\n\n '{{ ($json.account_name ?? \"\").replaceAll(\"'\", \"''\") }}',\n '{{ ($json.account_code ?? \"\").replaceAll(\"'\", \"''\") }}',\n '{{ ($json.consignment_type ?? \"\").replaceAll(\"'\", \"''\") }}',\n '{{ ($json.classification ?? \"\").replaceAll(\"'\", \"''\") }}',\n\n '{{ ($json.service_type ?? \"\").replaceAll(\"'\", \"''\") }}',\n '{{ ($json.service_code ?? \"\").replaceAll(\"'\", \"''\") }}',\n '{{ ($json.service_surcharge ?? \"\").replaceAll(\"'\", \"''\") }}',\n\n '{{ ($json.collection_service_code ?? \"\").replaceAll(\"'\", \"''\") }}',\n '{{ ($json.collection_service_surcharge ?? \"\").replaceAll(\"'\", \"''\") }}',\n '{{ ($json.delivery_service_code ?? \"\").replaceAll(\"'\", \"''\") }}',\n '{{ ($json.delivery_service_surcharge ?? \"\").replaceAll(\"'\", \"''\") }}',\n\n {{ $json.due_date ? \"'\" + $json.due_date + \"'\" : \"null\" }},\n {{ $json.due_time ? \"'\" + $json.due_time + \"'\" : \"null\" }},\n\n {{ $json.planned_collection_date ? \"'\" + $json.planned_collection_date + \"'\" : \"null\" }},\n {{ $json.planned_delivery_date ? \"'\" + $json.planned_delivery_date + \"'\" : \"null\" }},\n {{ $json.delivery_list_date ? \"'\" + $json.delivery_list_date + \"'\" : \"null\" }},\n {{ $json.collection_list_date ? \"'\" + $json.collection_list_date + \"'\" : \"null\" }},\n {{ $json.manifested_at ? \"'\" + $json.manifested_at + \"'\" : \"null\" }},\n\n {{ $json.lifts ?? \"null\" }},\n {{ $json.weight_kg ?? \"null\" }},\n '{{ ($json.bill_unit_type ?? \"\").replaceAll(\"'\", \"''\") }}',\n {{ $json.bill_unit_amount ?? \"null\" }},\n {{ $json.pallet_count ?? \"null\" }},\n\n {{ $json.handball === null || $json.handball === undefined ? \"null\" : ($json.handball ? \"true\" : \"false\") }},\n {{ $json.tail_lift === null || $json.tail_lift === undefined ? \"null\" : ($json.tail_lift ? \"true\" : \"false\") }},\n {{ $json.adr_goods === null || $json.adr_goods === undefined ? \"null\" : ($json.adr_goods ? \"true\" : \"false\") }},\n {{ $json.limited_quantity_goods === null || $json.limited_quantity_goods === undefined ? \"null\" : ($json.limited_quantity_goods ? \"true\" : \"false\") }},\n {{ $json.booked_in === null || $json.booked_in === undefined ? \"null\" : ($json.booked_in ? \"true\" : \"false\") }},\n {{ $json.book_in_request === null || $json.book_in_reLine truncated
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.postgres",
|
||||
"typeVersion": 2.6,
|
||||
"position": [
|
||||
-5440,
|
||||
-640
|
||||
],
|
||||
"id": "aeea14bb-f715-42ec-a0a6-892a6fdb89c5",
|
||||
"name": "Upsert Palletways BI",
|
||||
"credentials": {
|
||||
"postgres": {
|
||||
"id": "a0jnANQ89VLSby0b",
|
||||
"name": "Palletways DB"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const out = [];\nconst seen = new Set();\n\nconst asArray = (v) => {\n if (v === undefined || v === null || v === '') return [];\n return Array.isArray(v) ? v : [v];\n};\n\nconst text = (v) => {\n if (v === undefined || v === null) return '';\n return String(v).trim();\n};\n\nconst lastRunTimestamp =\n $items(\"Parse Last Timestamp\")[0]?.json?.lastRunTimestamp;\n\nif (!lastRunTimestamp) {\n throw new Error('lastRunTimestamp ontbreekt vanuit Parse Last Timestamp');\n}\n\nconst lastRunDate = new Date(lastRunTimestamp);\nconst response = $json.Response;\n\nconst statusCode = response?.Status?.Code;\nconst description = response?.Status?.Description;\nconst count = Number(response?.Status?.Count ?? 0);\nconst data = response?.Detail?.Data;\n\nif (statusCode === 'NO_RESULTS_FOUND' || count === 0 || !data) {\n return [{\n json: {\n no_work: true,\n reason: description || 'Geen collections gevonden in Palletways response',\n palletways_status_code: statusCode || null,\n palletways_count: count,\n lastRunTimestamp,\n checked_at: new Date().toISOString()\n }\n }];\n}\n\nif (statusCode !== 'OK') {\n throw new Error(`Palletways response niet OK: ${statusCode || 'onbekend'} - ${description || ''}`);\n}\n\nconst dataItems = asArray(data);\n\nfor (const entry of dataItems) {\n const manifest = entry?.Manifest;\n if (!manifest) continue;\n\n const dateStr = text(manifest.Date);\n const timeStr = text(manifest.Time);\n if (!dateStr || !timeStr) continue;\n\n const manifestDateTime = `${dateStr}T${timeStr}`;\n const manifestDate = new Date(manifestDateTime);\n\n if (!(manifestDate > lastRunDate)) {\n continue;\n }\n\n const accounts = asArray(manifest?.Depot?.Account);\n\n for (const account of accounts) {\n const consignments = asArray(account?.Consignment);\n\n for (const c of consignments) {\n const trackingid = text(c?.TrackingID);\n if (!trackingid || seen.has(trackingid)) continue;\n seen.add(trackingid);\n\n out.push({\n json: {\n trackingid,\n source_name: 'PW_COLLECTION_FOR_US',\n collection_list_date: dateStr,\n manifestDateTime,\n manifest_date: dateStr,\n manifest_time: timeStr,\n planned_collection_date: c?.ColDueDate || c?.DueDate || null,\n collection_depot: c?.CollectionDepot ?? null,\n delivery_depot: c?.DeliveryDepot ?? null,\n paying_depot: manifest?.Depot?.Number ?? null\n }\n });\n }\n }\n}\n\nif (out.length === 0) {\n return [{\n json: {\n no_work: true,\n reason: 'Geen nieuwe collections na lastRunTimestamp',\n lastRunTimestamp,\n checked_at: new Date().toISOString()\n }\n }];\n}\n\nreturn out;"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
-6608,
|
||||
-640
|
||||
],
|
||||
"id": "5c00f764-7042-43eb-b12a-cb7b8fc7fd39",
|
||||
"name": "Extract Collections or No Work"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict",
|
||||
"version": 2
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "824aea27-1485-4cd4-a3e5-061873540857",
|
||||
"leftValue": "={{ $json.trackingid ? 'yes' : 'no' }}",
|
||||
"rightValue": "yes",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals",
|
||||
"name": "filter.operator.equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.2,
|
||||
"position": [
|
||||
-6384,
|
||||
-640
|
||||
],
|
||||
"id": "be067f44-a440-4acc-a544-61f56bb2d708",
|
||||
"name": "Has TrackingID?"
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"Schedule Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get Last Timestamp from Redis",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Last Timestamp from Redis": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Parse Last Timestamp",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Parse Last Timestamp": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get Collections",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Collections": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Parse XML Response",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Parse XML Response": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Collections or No Work",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Collections or No Work": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Has TrackingID?",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Has TrackingID?": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Process Each Consignment",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Prepare Timestamp Data",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Process Each Consignment": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Prepare Timestamp Data",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Get Consignment Details",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Consignment Details": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Normalize Palletways Consignment",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Normalize Palletways Consignment": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Upsert Palletways BI",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Upsert Palletways BI": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Process Each Consignment",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Prepare Timestamp Data": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Save Timestamp to Redis",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": true,
|
||||
"settings": {
|
||||
"executionOrder": "v1",
|
||||
"binaryMode": "separate",
|
||||
"timezone": "Europe/London",
|
||||
"callerPolicy": "workflowsFromSameOwner",
|
||||
"availableInMCP": false,
|
||||
"timeSavedMode": "fixed",
|
||||
"saveDataSuccessExecution": "all"
|
||||
},
|
||||
"versionId": "aa30be27-5af1-4866-9117-cfae9ad48a7d",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "bef8d409866a58c0777dfe7cca1b9c2400fd051c056d361501393ab423006b5f"
|
||||
},
|
||||
"nodeGroups": [],
|
||||
"id": "joBBlyPunxnCOu4R",
|
||||
"tags": [
|
||||
{
|
||||
"updatedAt": "2025-11-11T04:59:39.416Z",
|
||||
"createdAt": "2025-11-11T04:59:39.416Z",
|
||||
"id": "ClOZrAjJKoGLFvaO",
|
||||
"name": "Palletways"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user