a27ee59125
Test backoffice (smb-crm) / test (push) Has been cancelled
Flask fires a fire-and-forget internal webhook (owner_notify.py, mirroring
mailer.py's background-thread queue) on booking create/cancel/reschedule,
carrying the same {booking, business_name, notify_channel} shape the old
EA-driven "Build booking row" node produced, plus an event field so the
Telegram message can say what actually happened. n8n/booking-sync.json gets
a new webhook + IF node feeding the existing Telegram node directly, so it
no longer needs EA's API to build the notification payload; channel routing
(only telegram sends for now) lives in that IF node.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
194 lines
6.5 KiB
JSON
194 lines
6.5 KiB
JSON
{
|
|
"name": "SMB · Booking Sync",
|
|
"nodes": [
|
|
{
|
|
"parameters": {
|
|
"httpMethod": "POST",
|
|
"path": "booking",
|
|
"responseMode": "onReceived",
|
|
"options": {
|
|
"allowedOrigins": "*"
|
|
}
|
|
},
|
|
"id": "b0000000-1111-2222-3333-444444444444",
|
|
"name": "Booking webhook",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
240,
|
|
300
|
|
],
|
|
"webhookId": "smb-booking-webhook-0001"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const w=$input.first().json; const b=w.body||w;\nconst s=v=>{v=(v==null?'':String(v));return /^[=+\\-@\\t\\r]/.test(v)?\"'\"+v:v;};\nconst EA='http://easyappointments';\nconst AUTH='Basic __EA_AUTH__';\nconst get=p=>this.helpers.httpRequest({method:'GET',url:EA+'/index.php/api/v1/'+p,headers:{Authorization:AUTH},json:true});\nlet booking, business_name='', notify_channel='telegram';\nif(b.action&&b.payload){\n const p=b.payload;\n const r=await Promise.all([get('customers/'+p.id_users_customer),get('services/'+p.id_services),get('providers/'+p.id_users_provider)]);\n const cust=r[0], svc=r[1], prov=r[2];\n const tz=prov.timezone||'UTC';\n const toUtc=dt=>{ if(!dt) return null; const d=DateTime.fromFormat(String(dt),'yyyy-MM-dd HH:mm:ss',{zone:tz}); return d.isValid?d.toUTC().toISO():String(dt).replace(' ','T'); };\n booking={booking_id:'EA-'+p.id,client_id:String(prov.notes||'').trim(),\n customer_name:s(((cust.firstName||'')+' '+(cust.lastName||'')).trim()),\n customer_contact:s(cust.email||cust.phone||''),service:s(svc.name),\n start_time:toUtc(p.start_datetime),end_time:toUtc(p.end_datetime),\n source:'easyappointments',status:p.status||'confirmed'};\n business_name=s((prov.firstName||'')+' '+(prov.lastName||''));\n} else {\n booking={booking_id:b.booking_id||'B-'+Date.now(),client_id:s(b.client_id),\n customer_name:s(b.customer_name||b.name),customer_contact:s(b.customer_contact||b.contact||b.email||b.phone),\n service:s(b.service||b.service_interest),start_time:b.start_time||null,end_time:b.end_time||null,\n source:s(b.source||'manual'),status:s(b.status||'confirmed')};\n business_name=s(b.business_name); notify_channel=s(b.notify_channel||'telegram');\n}\nreturn [{json:{booking,business_name,notify_channel}}];"
|
|
},
|
|
"id": "b0000000-1111-2222-3333-555555555555",
|
|
"name": "Build booking row",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
460,
|
|
300
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "http://smb-crm:8080/api/bookings",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "X-CRM-Token",
|
|
"value": "__CRM_TOKEN__"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ JSON.stringify($json.booking) }}",
|
|
"options": {}
|
|
},
|
|
"id": "b0000000-1111-2222-3333-666666666666",
|
|
"name": "Save to CRM (bookings)",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
680,
|
|
300
|
|
],
|
|
"retryOnFail": true,
|
|
"maxTries": 4,
|
|
"waitBetweenTries": 3000
|
|
},
|
|
{
|
|
"parameters": {
|
|
"resource": "message",
|
|
"operation": "sendMessage",
|
|
"chatId": "5499280257",
|
|
"text": "=📅 {{ $json.body.event === 'cancelled' ? 'Buchung storniert' : $json.body.event === 'rescheduled' ? 'Buchung verschoben' : 'Neue Buchung' }}\nBetrieb: {{ $json.body.business_name || $json.body.booking.client_id }}\nKunde: {{ $json.body.booking.customer_name || '—' }}\nLeistung: {{ $json.body.booking.service || '—' }}\nWann: {{ $json.body.booking.start_time || '—' }}\nKontakt: {{ $json.body.booking.customer_contact || '—' }}\nQuelle: {{ $json.body.booking.source }}",
|
|
"additionalFields": {
|
|
"appendAttribution": false
|
|
}
|
|
},
|
|
"id": "b0000000-1111-2222-3333-777777777777",
|
|
"name": "Notify owner (Telegram)",
|
|
"type": "n8n-nodes-base.telegram",
|
|
"typeVersion": 1.2,
|
|
"position": [
|
|
900,
|
|
500
|
|
],
|
|
"credentials": {
|
|
"telegramApi": {
|
|
"id": "d9s4Ec5ocEH8FAxh",
|
|
"name": "SMB Telegram (leads bot)"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"httpMethod": "POST",
|
|
"path": "owner-notify",
|
|
"responseMode": "onReceived",
|
|
"options": {
|
|
"allowedOrigins": "*"
|
|
}
|
|
},
|
|
"id": "b0000000-1111-2222-3333-888888888888",
|
|
"name": "Owner notify webhook",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
240,
|
|
500
|
|
],
|
|
"webhookId": "smb-owner-notify-webhook-0001"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": "",
|
|
"typeValidation": "strict"
|
|
},
|
|
"conditions": [
|
|
{
|
|
"id": "b0000000-1111-2222-3333-aaaaaaaaaaaa",
|
|
"leftValue": "={{ $json.body.notify_channel }}",
|
|
"rightValue": "telegram",
|
|
"operator": {
|
|
"type": "string",
|
|
"operation": "equals"
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and"
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "b0000000-1111-2222-3333-999999999999",
|
|
"name": "Telegram channel?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
460,
|
|
500
|
|
]
|
|
}
|
|
],
|
|
"connections": {
|
|
"Booking webhook": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Build booking row",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Build booking row": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Save to CRM (bookings)",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Owner notify webhook": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Telegram channel?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Telegram channel?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Notify owner (Telegram)",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"settings": {
|
|
"executionOrder": "v1",
|
|
"callerPolicy": "workflowsFromSameOwner",
|
|
"availableInMCP": false
|
|
}
|
|
} |