Remove Google Sheets mirror entirely (#13)
Test backoffice (smb-crm) / test (push) Has been cancelled
Test backoffice (smb-crm) / test (push) Has been cancelled
Postgres is now the sole source of truth: delete sheets.py and import_from_sheets.py, strip mirror_entity/mirror_async/_mirror_worker and POST /api/sync from app.py, drop the tab/mirror keys from db.py's TABLES. Re-point n8n/renewal-reminder.json at the CRM's own HTTP API (GET /api/clients, POST /api/activity_log) instead of the Sheets nodes, and drop SHEET_ID/GOOGLE_SA_JSON from deploy env/compose and requests from requirements.txt (PyJWT stays — still used by booking_api.py). Updates docs/README/playbooks accordingly and closes the old #5 (atomic mirror) as moot. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+26
-57
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "SMB \u00b7 Renewal Reminder",
|
||||
"name": "SMB · Renewal Reminder",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
@@ -23,43 +23,24 @@
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"authentication": "serviceAccount",
|
||||
"resource": "sheet",
|
||||
"operation": "read",
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"value": "1raMSWRZw_JfHlWqOb3LbhaQ6LWx0VGblxIV4Z2pSzp8",
|
||||
"mode": "id"
|
||||
},
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"value": "470934735",
|
||||
"mode": "list",
|
||||
"cachedResultName": "Clients"
|
||||
},
|
||||
"url": "http://smb-crm:8080/api/clients",
|
||||
"options": {}
|
||||
},
|
||||
"id": "f06a60af-dff1-415d-aca6-fef0367f3553",
|
||||
"name": "Read Clients",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"typeVersion": 4.5,
|
||||
"name": "Read Clients (DB)",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
460,
|
||||
300
|
||||
],
|
||||
"credentials": {
|
||||
"googleApi": {
|
||||
"id": "45TWPyl1wVk2Vxdm",
|
||||
"name": "SMB Google (service account)"
|
||||
}
|
||||
},
|
||||
"retryOnFail": true,
|
||||
"maxTries": 4,
|
||||
"waitBetweenTries": 3000
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "\nconst rows = $input.all().map(i=>i.json);\nconst today=new Date();\nconst t0=Date.UTC(today.getUTCFullYear(),today.getUTCMonth(),today.getUTCDate());\nfunction parseDate(v){\n if(v==null||v==='') return null;\n const s=String(v).trim();\n if(/^\\d+(\\.\\d+)?$/.test(s)){ return Date.UTC(1899,11,30)+Number(s)*86400000; }\n const m=s.match(/^(\\d{4})-(\\d{2})-(\\d{2})/);\n if(m) return Date.UTC(+m[1],+m[2]-1,+m[3]);\n const d=Date.parse(s); return isNaN(d)?null:d;\n}\nconst offsets=[14,7,3,1,0];\nconst due=[];\nfor(const r of rows){\n const status=String(r.status||'').toLowerCase();\n if(status!=='active') continue;\n const rd=parseDate(r.renewal_date);\n if(rd==null) continue;\n const days=Math.round((rd-t0)/86400000);\n if(days<0 || offsets.includes(days)){\n due.push({client_id:r.client_id, business:r.business_name, renewal_date:r.renewal_date,\n days, fee:r.monthly_fee_eur, cycle:r.billing_cycle});\n }\n}\nif(due.length===0) return [];\ndue.sort((a,b)=>a.days-b.days);\nconst line=d=>`\u2022 ${d.business||d.client_id} \u2014 ${d.days<0?('\u00dcBERF\u00c4LLIG seit '+(-d.days)+'T'):(d.days===0?'heute':('in '+d.days+'T'))} (${d.renewal_date}${d.fee?(', '+d.fee+'\u20ac/'+(d.cycle||'')):''})`;\nconst summary='\ud83d\udd14 Anstehende Verl\u00e4ngerungen ('+due.length+')\\n'+due.map(line).join('\\n');\nreturn [{json:{count:due.length, summary, due}}];\n"
|
||||
"jsCode": "\nconst rows = $input.first().json.rows || [];\nconst today=new Date();\nconst t0=Date.UTC(today.getUTCFullYear(),today.getUTCMonth(),today.getUTCDate());\nfunction parseDate(v){\n if(v==null||v==='') return null;\n const s=String(v).trim();\n const m=s.match(/^(\\d{4})-(\\d{2})-(\\d{2})/);\n if(m) return Date.UTC(+m[1],+m[2]-1,+m[3]);\n const d=Date.parse(s); return isNaN(d)?null:d;\n}\nconst offsets=[14,7,3,1,0];\nconst due=[];\nfor(const r of rows){\n const status=String(r.status||'').toLowerCase();\n if(status!=='active') continue;\n const rd=parseDate(r.renewal_date);\n if(rd==null) continue;\n const days=Math.round((rd-t0)/86400000);\n if(days<0 || offsets.includes(days)){\n due.push({client_id:r.client_id, business:r.business_name, renewal_date:r.renewal_date,\n days, fee:r.monthly_fee_eur, cycle:r.billing_cycle});\n }\n}\nif(due.length===0) return [];\ndue.sort((a,b)=>a.days-b.days);\nconst line=d=>`• ${d.business||d.client_id} — ${d.days<0?('ÜBERFÄLLIG seit '+(-d.days)+'T'):(d.days===0?'heute':('in '+d.days+'T'))} (${d.renewal_date}${d.fee?(', '+d.fee+'€/'+(d.cycle||'')):''})`;\nconst summary='🔔 Anstehende Verlängerungen ('+due.length+')\\n'+due.map(line).join('\\n');\nreturn [{json:{count:due.length, summary, due}}];\n"
|
||||
},
|
||||
"id": "9f3e4ca9-0d87-4d1c-9f5e-abdf5f9f77dd",
|
||||
"name": "Find due renewals",
|
||||
@@ -110,42 +91,30 @@
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"authentication": "serviceAccount",
|
||||
"resource": "sheet",
|
||||
"operation": "append",
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"value": "1raMSWRZw_JfHlWqOb3LbhaQ6LWx0VGblxIV4Z2pSzp8",
|
||||
"mode": "id"
|
||||
},
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"value": "170940481",
|
||||
"mode": "list",
|
||||
"cachedResultName": "Activity Log"
|
||||
},
|
||||
"columns": {
|
||||
"mappingMode": "autoMapInputData",
|
||||
"value": {},
|
||||
"matchingColumns": [],
|
||||
"schema": []
|
||||
"method": "POST",
|
||||
"url": "http://smb-crm:8080/api/activity_log",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "X-CRM-Token",
|
||||
"value": "__CRM_TOKEN__"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify($json) }}",
|
||||
"options": {}
|
||||
},
|
||||
"id": "55978962-489b-4740-a242-7470f08087b6",
|
||||
"name": "Append to Activity Log",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"typeVersion": 4.5,
|
||||
"name": "Append to Activity Log (DB)",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
1340,
|
||||
300
|
||||
],
|
||||
"credentials": {
|
||||
"googleApi": {
|
||||
"id": "45TWPyl1wVk2Vxdm",
|
||||
"name": "SMB Google (service account)"
|
||||
}
|
||||
},
|
||||
"retryOnFail": true,
|
||||
"maxTries": 4,
|
||||
"waitBetweenTries": 3000
|
||||
@@ -156,14 +125,14 @@
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Read Clients",
|
||||
"node": "Read Clients (DB)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Read Clients": {
|
||||
"Read Clients (DB)": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
@@ -200,7 +169,7 @@
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Append to Activity Log",
|
||||
"node": "Append to Activity Log (DB)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
@@ -213,4 +182,4 @@
|
||||
"callerPolicy": "workflowsFromSameOwner",
|
||||
"availableInMCP": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user