From ce373afb8890eabdbebf93ffac09e5794d78908d Mon Sep 17 00:00:00 2001 From: Aewing Date: Mon, 1 Dec 2025 23:28:07 -0600 Subject: [PATCH] Update excel export and value coersion to pb field expectations --- backend/config.json | 1 - backend/excelsync.log | 1975 +---------------- backend/src/excelsync.js | 478 ++-- backend/src/excelsync.log | 0 .../utilities/sharepointShareLinktoGraph.html | 1 + 5 files changed, 264 insertions(+), 2191 deletions(-) delete mode 100644 backend/src/excelsync.log diff --git a/backend/config.json b/backend/config.json index d03f9f4..f7e536c 100644 --- a/backend/config.json +++ b/backend/config.json @@ -1,7 +1,6 @@ { "tenantId": "3fd97ea7-b124-41f1-855f-52d8ac3b16c7", "clientId": "3c846e71-9609-40e1-b458-0eb805e21b9f", - "sharepointFileUrl": "https://czflex.sharepoint.com/:x:/r/sites/Team/Shared%20Documents/General/JOBS%201000-9999.xls", "pbUrl": "http://10.10.1.109:8080", "pbCollection": "Job_Info_TestEnv", "pbEmail": "pbserviceupdate@cardoza.construction" diff --git a/backend/excelsync.log b/backend/excelsync.log index b0308e5..4260418 100644 --- a/backend/excelsync.log +++ b/backend/excelsync.log @@ -1,1918 +1,63 @@ -🔵 Checking token acquisition and excel readiness -✅ Graph token acquired -✅ PocketBase token acquired -❌ Error, not ready for updating.exceltopb is not defined -🔵 Checking token acquisition and excel readiness -✅ Graph token acquired -✅ PocketBase token acquired -✅ Ready for updating! -🔵 Checking token acquisition and excel readiness -✅ Graph token acquired -✅ PocketBase token acquired -❌ Failed to fetch schema: The authorized record is not allowed to perform this action. -❌ Error, not ready for updating.The authorized record is not allowed to perform this action. -🔵 Checking token acquisition and excel readiness -✅ Graph token acquired -❌ Error, not ready for updating.pbEmail2 is not defined -🔵 Checking token acquisition and excel readiness -✅ Graph token acquired -❌ Error, not ready for updating.Failed to authenticate. -🔵 Checking token acquisition and excel readiness -✅ Graph token acquired -❌ Error, not ready for updating.Missing or invalid collection context. -🔵 Checking token acquisition and excel readiness -✅ Graph token acquired -❌ Error, not ready for updating.Failed to authenticate. -🔵 Checking token acquisition and excel readiness -✅ Graph token acquired -✅ PocketBase token acquired -✅ Schema loaded for Job_Info_TestEnv -[ - { - "autogeneratePattern": "[a-z0-9]{15}", - "hidden": false, - "id": "text3208210256", - "max": 15, - "min": 15, - "name": "id", - "pattern": "^[a-z0-9]+$", - "presentable": false, - "primaryKey": true, - "required": true, - "system": true, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2388997171", - "max": 0, - "min": 0, - "name": "Job_Number", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2378820285", - "max": 0, - "min": 0, - "name": "Job_Full_Name", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2158740783", - "max": 0, - "min": 0, - "name": "Job_Name", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text3732958801", - "max": 0, - "min": 0, - "name": "Job_Address", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1381428224", - "max": 0, - "min": 0, - "name": "Job_Type", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1908819108", - "max": 0, - "min": 0, - "name": "Flag", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1677020795", - "max": 0, - "min": 0, - "name": "Job_Status", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2518113217", - "max": 0, - "min": 0, - "name": "Job_Division", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2325867089", - "max": 0, - "min": 0, - "name": "Estimator", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text3229868610", - "max": 0, - "min": 0, - "name": "Office_Rep", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text899226661", - "max": 0, - "min": 0, - "name": "Manager", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text806559182", - "max": 0, - "min": 0, - "name": "Company_Client", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1854315289", - "max": 0, - "min": 0, - "name": "Contact_Person", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1956405909", - "max": 0, - "min": 0, - "name": "Phone_Number", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text642995056", - "max": 0, - "min": 0, - "name": "Email", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text3208080729", - "max": 0, - "min": 0, - "name": "Due_Date", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text4112652156", - "max": 0, - "min": 0, - "name": "Due_Date_Source", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1362567553", - "max": 0, - "min": 0, - "name": "Due_Date_Counter", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2050867814", - "max": 0, - "min": 0, - "name": "Due_Time", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1613691138", - "max": 0, - "min": 0, - "name": "Docs_Uploaded", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text701636273", - "max": 0, - "min": 0, - "name": "QB_Created", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text159074814", - "max": 0, - "min": 0, - "name": "Added_To_Calendar", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2443123173", - "max": 0, - "min": 0, - "name": "PSwift_Uploaded", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1028487862", - "max": 0, - "min": 0, - "name": "Voxer_Created", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2654168435", - "max": 0, - "min": 0, - "name": "Estimate_Reviewed", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1033433223", - "max": 0, - "min": 0, - "name": "Estimate_Draft", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text4143394575", - "max": 0, - "min": 0, - "name": "Estimate_Approved", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2051602006", - "max": 0, - "min": 0, - "name": "Start_Date", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text811897623", - "max": 0, - "min": 0, - "name": "Schedule_Confidence", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text3235547528", - "max": 0, - "min": 0, - "name": "Notes", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1270095775", - "max": 0, - "min": 0, - "name": "Job_QB_Link", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text957490280", - "max": 0, - "min": 0, - "name": "Voxer_Link", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2814497178", - "max": 0, - "min": 0, - "name": "Job_Codes", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text275762613", - "max": 0, - "min": 0, - "name": "Submission_Date", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1286797620", - "max": 0, - "min": 0, - "name": "Active", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text3795106522", - "max": 0, - "min": 0, - "name": "Job_Folder_Link", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text282225560", - "max": 0, - "min": 0, - "name": "Tax_Exempt", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "hidden": false, - "id": "bool545115032", - "name": "Has_Attachments", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "json2488040839", - "maxSize": 0, - "name": "Attachment_List", - "presentable": false, - "required": false, - "system": false, - "type": "json" - }, - { - "cascadeDelete": false, - "collectionId": "pbc_194787171", - "hidden": false, - "id": "relation2779200245", - "maxSelect": 999, - "minSelect": 0, - "name": "Note_Ref", - "presentable": false, - "required": false, - "system": false, - "type": "relation" - }, - { - "hidden": false, - "id": "json4080242417", - "maxSize": 0, - "name": "latest_note_summary", - "presentable": false, - "required": false, - "system": false, - "type": "json" - }, - { - "hidden": false, - "id": "json2575510732", - "maxSize": 0, - "name": "note_thread_text", - "presentable": false, - "required": false, - "system": false, - "type": "json" - }, - { - "hidden": false, - "id": "number3771328437", - "max": null, - "min": null, - "name": "note_count", - "onlyInt": false, - "presentable": false, - "required": false, - "system": false, - "type": "number" - }, - { - "hidden": false, - "id": "date2500832881", - "max": "", - "min": "", - "name": "last_note_at", - "presentable": false, - "required": false, - "system": false, - "type": "date" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2569355707", - "max": 0, - "min": 0, - "name": "Job_Number_Parent", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2730408412", - "max": 0, - "min": 0, - "name": "Tax_Exempt_Docs_Uploaded", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text725757662", - "max": 0, - "min": 0, - "name": "Project_Manager", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "hidden": false, - "id": "autodate2990389176", - "name": "created", - "onCreate": true, - "onUpdate": false, - "presentable": false, - "system": false, - "type": "autodate" - }, - { - "hidden": false, - "id": "autodate3332085495", - "name": "updated", - "onCreate": true, - "onUpdate": true, - "presentable": false, - "system": false, - "type": "autodate" - } -] -✅ Ready for updating! -🔵 Checking token acquisition and excel readiness -✅ Graph token acquired -✅ PocketBase token acquired -✅ Schema loaded for Job_Info_TestEnv -[ - { - "autogeneratePattern": "[a-z0-9]{15}", - "hidden": false, - "id": "text3208210256", - "max": 15, - "min": 15, - "name": "id", - "pattern": "^[a-z0-9]+$", - "presentable": false, - "primaryKey": true, - "required": true, - "system": true, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2388997171", - "max": 0, - "min": 0, - "name": "Job_Number", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2378820285", - "max": 0, - "min": 0, - "name": "Job_Full_Name", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2158740783", - "max": 0, - "min": 0, - "name": "Job_Name", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text3732958801", - "max": 0, - "min": 0, - "name": "Job_Address", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1381428224", - "max": 0, - "min": 0, - "name": "Job_Type", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1677020795", - "max": 0, - "min": 0, - "name": "Job_Status", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2518113217", - "max": 0, - "min": 0, - "name": "Job_Division", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2325867089", - "max": 0, - "min": 0, - "name": "Estimator", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text3229868610", - "max": 0, - "min": 0, - "name": "Office_Rep", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text899226661", - "max": 0, - "min": 0, - "name": "Project_Manager", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text806559182", - "max": 0, - "min": 0, - "name": "Company_Client", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1854315289", - "max": 0, - "min": 0, - "name": "Contact_Person", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1956405909", - "max": 0, - "min": 0, - "name": "Phone_Number", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text642995056", - "max": 0, - "min": 0, - "name": "Email", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text4112652156", - "max": 0, - "min": 0, - "name": "Due_Date_Source", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1362567553", - "max": 0, - "min": 0, - "name": "Due_Date_Counter", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2050867814", - "max": 0, - "min": 0, - "name": "Due_Time", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text811897623", - "max": 0, - "min": 0, - "name": "Schedule_Confidence", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text3235547528", - "max": 0, - "min": 0, - "name": "Notes", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1270095775", - "max": 0, - "min": 0, - "name": "Job_QB_Link", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text957490280", - "max": 0, - "min": 0, - "name": "Voxer_Link", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2814497178", - "max": 0, - "min": 0, - "name": "Job_Codes", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text3795106522", - "max": 0, - "min": 0, - "name": "Job_Folder_Link", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "hidden": false, - "id": "bool545115032", - "name": "Has_Attachments", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "json2488040839", - "maxSize": 0, - "name": "Attachment_List", - "presentable": false, - "required": false, - "system": false, - "type": "json" - }, - { - "cascadeDelete": false, - "collectionId": "pbc_194787171", - "hidden": false, - "id": "relation2779200245", - "maxSelect": 999, - "minSelect": 0, - "name": "Note_Ref", - "presentable": false, - "required": false, - "system": false, - "type": "relation" - }, - { - "hidden": false, - "id": "json4080242417", - "maxSize": 0, - "name": "latest_note_summary", - "presentable": false, - "required": false, - "system": false, - "type": "json" - }, - { - "hidden": false, - "id": "json2575510732", - "maxSize": 0, - "name": "note_thread_text", - "presentable": false, - "required": false, - "system": false, - "type": "json" - }, - { - "hidden": false, - "id": "number3771328437", - "max": null, - "min": null, - "name": "note_count", - "onlyInt": false, - "presentable": false, - "required": false, - "system": false, - "type": "number" - }, - { - "hidden": false, - "id": "date2500832881", - "max": "", - "min": "", - "name": "last_note_at", - "presentable": false, - "required": false, - "system": false, - "type": "date" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2569355707", - "max": 0, - "min": 0, - "name": "Job_Number_Parent", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "hidden": false, - "id": "date2051602006", - "max": "", - "min": "", - "name": "Start_Date", - "presentable": false, - "required": false, - "system": false, - "type": "date" - }, - { - "hidden": false, - "id": "date275762613", - "max": "", - "min": "", - "name": "Submission_Date", - "presentable": false, - "required": false, - "system": false, - "type": "date" - }, - { - "hidden": false, - "id": "bool1908819108", - "name": "Flag", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool282225560", - "name": "Tax_Exempt", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool1286797620", - "name": "Active", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "date3208080729", - "max": "", - "min": "", - "name": "Due_Date", - "presentable": false, - "required": false, - "system": false, - "type": "date" - }, - { - "hidden": false, - "id": "bool1523500494", - "name": "Docs_Uploaded", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool3561832989", - "name": "Tax_Exempt_Docs_Uploaded", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool4143394575", - "name": "Estimate_Approved", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool701636273", - "name": "QB_Created", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool159074814", - "name": "Added_To_Calendar", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool2443123173", - "name": "PSwift_Uploaded", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool1028487862", - "name": "Voxer_Created", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool2654168435", - "name": "Estimate_Reviewed", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool1033433223", - "name": "Estimate_Draft", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "autodate2990389176", - "name": "created", - "onCreate": true, - "onUpdate": false, - "presentable": false, - "system": false, - "type": "autodate" - }, - { - "hidden": false, - "id": "autodate3332085495", - "name": "updated", - "onCreate": true, - "onUpdate": true, - "presentable": false, - "system": false, - "type": "autodate" - } -] -✅ Ready for updating! -🔵 Checking token acquisition and excel readiness -✅ Graph token acquired -✅ PocketBase token acquired -✅ Schema loaded for Job_Info_TestEnv -[ - { - "autogeneratePattern": "[a-z0-9]{15}", - "hidden": false, - "id": "text3208210256", - "max": 15, - "min": 15, - "name": "id", - "pattern": "^[a-z0-9]+$", - "presentable": false, - "primaryKey": true, - "required": true, - "system": true, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2388997171", - "max": 0, - "min": 0, - "name": "Job_Number", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2378820285", - "max": 0, - "min": 0, - "name": "Job_Full_Name", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2158740783", - "max": 0, - "min": 0, - "name": "Job_Name", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text3732958801", - "max": 0, - "min": 0, - "name": "Job_Address", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1381428224", - "max": 0, - "min": 0, - "name": "Job_Type", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1677020795", - "max": 0, - "min": 0, - "name": "Job_Status", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2518113217", - "max": 0, - "min": 0, - "name": "Job_Division", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2325867089", - "max": 0, - "min": 0, - "name": "Estimator", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text3229868610", - "max": 0, - "min": 0, - "name": "Office_Rep", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text899226661", - "max": 0, - "min": 0, - "name": "Project_Manager", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text806559182", - "max": 0, - "min": 0, - "name": "Company_Client", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1854315289", - "max": 0, - "min": 0, - "name": "Contact_Person", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1956405909", - "max": 0, - "min": 0, - "name": "Phone_Number", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text642995056", - "max": 0, - "min": 0, - "name": "Email", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text4112652156", - "max": 0, - "min": 0, - "name": "Due_Date_Source", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1362567553", - "max": 0, - "min": 0, - "name": "Due_Date_Counter", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2050867814", - "max": 0, - "min": 0, - "name": "Due_Time", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text811897623", - "max": 0, - "min": 0, - "name": "Schedule_Confidence", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text3235547528", - "max": 0, - "min": 0, - "name": "Notes", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text1270095775", - "max": 0, - "min": 0, - "name": "Job_QB_Link", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text957490280", - "max": 0, - "min": 0, - "name": "Voxer_Link", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2814497178", - "max": 0, - "min": 0, - "name": "Job_Codes", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text3795106522", - "max": 0, - "min": 0, - "name": "Job_Folder_Link", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "hidden": false, - "id": "bool545115032", - "name": "Has_Attachments", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "json2488040839", - "maxSize": 0, - "name": "Attachment_List", - "presentable": false, - "required": false, - "system": false, - "type": "json" - }, - { - "cascadeDelete": false, - "collectionId": "pbc_194787171", - "hidden": false, - "id": "relation2779200245", - "maxSelect": 999, - "minSelect": 0, - "name": "Note_Ref", - "presentable": false, - "required": false, - "system": false, - "type": "relation" - }, - { - "hidden": false, - "id": "json4080242417", - "maxSize": 0, - "name": "latest_note_summary", - "presentable": false, - "required": false, - "system": false, - "type": "json" - }, - { - "hidden": false, - "id": "json2575510732", - "maxSize": 0, - "name": "note_thread_text", - "presentable": false, - "required": false, - "system": false, - "type": "json" - }, - { - "hidden": false, - "id": "number3771328437", - "max": null, - "min": null, - "name": "note_count", - "onlyInt": false, - "presentable": false, - "required": false, - "system": false, - "type": "number" - }, - { - "hidden": false, - "id": "date2500832881", - "max": "", - "min": "", - "name": "last_note_at", - "presentable": false, - "required": false, - "system": false, - "type": "date" - }, - { - "autogeneratePattern": "", - "hidden": false, - "id": "text2569355707", - "max": 0, - "min": 0, - "name": "Job_Number_Parent", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }, - { - "hidden": false, - "id": "date2051602006", - "max": "", - "min": "", - "name": "Start_Date", - "presentable": false, - "required": false, - "system": false, - "type": "date" - }, - { - "hidden": false, - "id": "date275762613", - "max": "", - "min": "", - "name": "Submission_Date", - "presentable": false, - "required": false, - "system": false, - "type": "date" - }, - { - "hidden": false, - "id": "bool1908819108", - "name": "Flag", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool282225560", - "name": "Tax_Exempt", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool1286797620", - "name": "Active", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "date3208080729", - "max": "", - "min": "", - "name": "Due_Date", - "presentable": false, - "required": false, - "system": false, - "type": "date" - }, - { - "hidden": false, - "id": "bool1523500494", - "name": "Docs_Uploaded", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool3561832989", - "name": "Tax_Exempt_Docs_Uploaded", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool4143394575", - "name": "Estimate_Approved", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool701636273", - "name": "QB_Created", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool159074814", - "name": "Added_To_Calendar", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool2443123173", - "name": "PSwift_Uploaded", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool1028487862", - "name": "Voxer_Created", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool2654168435", - "name": "Estimate_Reviewed", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "bool1033433223", - "name": "Estimate_Draft", - "presentable": false, - "required": false, - "system": false, - "type": "bool" - }, - { - "hidden": false, - "id": "autodate2990389176", - "name": "created", - "onCreate": true, - "onUpdate": false, - "presentable": false, - "system": false, - "type": "autodate" - }, - { - "hidden": false, - "id": "autodate3332085495", - "name": "updated", - "onCreate": true, - "onUpdate": true, - "presentable": false, - "system": false, - "type": "autodate" - } -] -✅ Ready for updating! -🔵 Checking token acquisition and excel readiness -✅ Graph token acquired -✅ PocketBase token acquired -❌ Error, not ready for updating.getPocketBaseSchema is not defined -🔵 Checking token acquisition and excel readiness -✅ Graph token acquired -❌ Error, not ready for updating. Failed to authenticate. -🔵 Checking token acquisition and excel readiness -✅ Graph token acquired -✅ PocketBase token acquired -❌ Error, not ready for updating.exceltopb is not defined -🔵 Checking token acquisition and excel readiness -✅ Graph token acquired -✅ PocketBase token acquired -❌ Error, not ready for updating. fetchExcelRange is not defined -🔵 Checking token acquisition and excel readiness -✅ Graph token acquired -✅ PocketBase token acquired -🔵 Checking token acquisition and excel readiness -✅ Graph token acquired -✅ PocketBase token acquired -🔵 Checking token acquisition and excel readiness -🔵 Requesting Graph token... -✅ Graph token acquired -🔵 Logging into PocketBase... -✅ PocketBase token acquired -🔵 Requesting usedRange for Job Sheet 🔵 Starting Excel Sync… -⏭️ Export server bypassed (ENABLE_EXPORT=false) +🚀 ExcelSync export server running at http://localhost:3000/ +👉 Open this URL in your browser to verify and download the parsed Excel file. +❌ Sync failed: Request failed with status code 401 +🔵 Starting Excel Sync… +🚀 ExcelSync export server running at http://localhost:3000/ +👉 Open this URL in your browser to verify and download the parsed Excel file. +❌ Sync failed: Request failed with status code 401 +🔵 Starting Excel Sync… +🔵 Starting Excel Sync… +🚀 ExcelSync export server running at http://localhost:3000/ +👉 Open this URL in your browser to verify and download the parsed Excel file. ✅ Graph token acquired ✅ PocketBase token acquired +Reading Excel file from Graph buffer +Mapped 38 columns +Job_Number,Job_Full_Name,Job_Name,Job Address,Job Type,Flag,Tax_Exempt,Job Status,Job Division,Estimator,Office Rep,Manager,Company/Client,Contact Person,Phone Number,Email,Due Date,Due Date Source,Due Date Counter,Due Time,Tax_Exempt_Docs_Uploaded,Docs Uploaded,QB Created,Added to Calendar,PSwift Uploaded,Voxer Created,Estimate Reviewed,Estimate Draft,Estimate Approved,Start Date,Schedule_Confidence,Notes,Job QB Link,Voxer Link,Job_Codes,Submission Date,Active,Job Folder Link,pb_id +❌ Sync failed: updateNotesInSharePoint is not defined +🔵 Starting Excel Sync… +🚀 ExcelSync export server running at http://localhost:3000/ +👉 Open this URL in your browser to verify and download the parsed Excel file. +✅ Graph token acquired +✅ PocketBase token acquired +Reading Excel file from Graph buffer +Mapped 38 columns +Job_Number,Job_Full_Name,Job_Name,Job Address,Job Type,Flag,Tax_Exempt,Job Status,Job Division,Estimator,Office Rep,Manager,Company/Client,Contact Person,Phone Number,Email,Due Date,Due Date Source,Due Date Counter,Due Time,Tax_Exempt_Docs_Uploaded,Docs Uploaded,QB Created,Added to Calendar,PSwift Uploaded,Voxer Created,Estimate Reviewed,Estimate Draft,Estimate Approved,Start Date,Schedule_Confidence,Notes,Job QB Link,Voxer Link,Job_Codes,Submission Date,Active,Job Folder Link,pb_id +✅ First part complete: tokens + Excel parsing ready +🔵 Starting Excel Sync… +🚀 ExcelSync export server running at http://localhost:3000/ +👉 Open this URL in your browser to verify and download the parsed Excel file. +✅ Graph token acquired +✅ PocketBase token acquired +Reading Excel file from Graph buffer +Mapped 38 columns +Job_Number,Job_Full_Name,Job_Name,Job Address,Job Type,Flag,Tax_Exempt,Job Status,Job Division,Estimator,Office Rep,Manager,Company/Client,Contact Person,Phone Number,Email,Due Date,Due Date Source,Due Date Counter,Due Time,Tax_Exempt_Docs_Uploaded,Docs Uploaded,QB Created,Added to Calendar,PSwift Uploaded,Voxer Created,Estimate Reviewed,Estimate Draft,Estimate Approved,Start Date,Schedule_Confidence,Notes,Job QB Link,Voxer Link,Job_Codes,Submission Date,Active,Job Folder Link,pb_id +✅ First part complete: tokens + Excel parsing ready +🔵 Starting Excel Sync… +🚀 ExcelSync export server running at http://localhost:3000/ +👉 Open this URL in your browser to verify and download the parsed Excel file. +✅ Graph token acquired +✅ PocketBase token acquired +Reading Excel file from Graph buffer +Mapped 38 columns +Job_Number,Job_Full_Name,Job_Name,Job Address,Job Type,Flag,Tax_Exempt,Job Status,Job Division,Estimator,Office Rep,Manager,Company/Client,Contact Person,Phone Number,Email,Due Date,Due Date Source,Due Date Counter,Due Time,Tax_Exempt_Docs_Uploaded,Docs Uploaded,QB Created,Added to Calendar,PSwift Uploaded,Voxer Created,Estimate Reviewed,Estimate Draft,Estimate Approved,Start Date,Schedule_Confidence,Notes,Job QB Link,Voxer Link,Job_Codes,Submission Date,Active,Job Folder Link,pb_id +✅ First part complete: tokens + Excel parsing ready +🔵 Starting Excel Sync… +🚀 ExcelSync export server running at http://localhost:3000/ +👉 Open this URL in your browser to verify and download the parsed Excel file. +✅ Graph token acquired +✅ PocketBase token acquired +Reading Excel file from Graph buffer +Mapped 38 columns +Job_Number,Job_Full_Name,Job_Name,Job Address,Job Type,Flag,Tax_Exempt,Job Status,Job Division,Estimator,Office Rep,Manager,Company/Client,Contact Person,Phone Number,Email,Due Date,Due Date Source,Due Date Counter,Due Time,Tax_Exempt_Docs_Uploaded,Docs Uploaded,QB Created,Added to Calendar,PSwift Uploaded,Voxer Created,Estimate Reviewed,Estimate Draft,Estimate Approved,Start Date,Schedule_Confidence,Notes,Job QB Link,Voxer Link,Job_Codes,Submission Date,Active,Job Folder Link,pb_id +✅ First part complete: tokens + Excel parsing ready +🔵 Starting Excel Sync… +🚀 ExcelSync export server running at http://localhost:3000/ +👉 Open this URL in your browser to verify and download the parsed Excel file. +✅ Graph token acquired +✅ PocketBase token acquired +Reading Excel file from Graph buffer +Mapped 38 columns +Job_Number,Job_Full_Name,Job_Name,Job Address,Job Type,Flag,Tax_Exempt,Job Status,Job Division,Estimator,Office Rep,Manager,Company/Client,Contact Person,Phone Number,Email,Due Date,Due Date Source,Due Date Counter,Due Time,Tax_Exempt_Docs_Uploaded,Docs Uploaded,QB Created,Added to Calendar,PSwift Uploaded,Voxer Created,Estimate Reviewed,Estimate Draft,Estimate Approved,Start Date,Schedule_Confidence,Notes,Job QB Link,Voxer Link,Job_Codes,Submission Date,Active,Job Folder Link,pb_id +✅ First part complete: tokens + Excel parsing ready diff --git a/backend/src/excelsync.js b/backend/src/excelsync.js index 29bb8ee..017222b 100644 --- a/backend/src/excelsync.js +++ b/backend/src/excelsync.js @@ -1,9 +1,13 @@ +//12/1/2025 Allows Excel download and conversion of headers mapped to pb headers. +//coersion added to match pb field types. however, may still have difficulty in assimilating certain date formats from excel to pb date fields. +//due to Excel storing as all caps for boolean fields // excelsync.js import { FIELD_CONFIG } from "./fieldConfig.js"; import fs from "fs"; import axios from "axios"; import qs from "qs"; import * as XLSX from "xlsx"; +import ExcelJS from "exceljs"; import dotenv from "dotenv"; import { serve } from "bun"; import PocketBase from "pocketbase" @@ -11,38 +15,17 @@ import PocketBase from "pocketbase" dotenv.config(); // ----------------------------- -// EXCEL PARSER -// ----------------------------- -const HEADER_ROW_INDEX = 2; // Row 3 -const DATA_START_ROW_INDEX = 3; // Row 4 - -// ----------------------------- -// CONFIG +// CONFIG FOR TOKEN ACQUISITION GRAPH API + POCKETBASE // ----------------------------- const CONFIG = JSON.parse(fs.readFileSync("config.json", "utf8")); const tenantId = CONFIG.tenantId; const clientId = CONFIG.clientId; -const sharepointFileUrl = CONFIG.sharepointFileUrl; // production Excel file URL const pbUrl = CONFIG.pbUrl; -const pbCollection = CONFIG.pbCollection || "Job_Info_TestEnv"; +const pbCollection = CONFIG.pbCollection; const pbEmail = CONFIG.pbEmail; - const clientSecret = process.env.CLIENT_SECRET; const pbPassword = process.env.PB_PASSWORD; -if (!tenantId || !clientId || !clientSecret || !sharepointFileUrl || !pbUrl || !pbEmail || !pbPassword) { - console.error("❌ Missing required config/env values"); - process.exit(1); -} -console.log({ - tenantId, - clientId, - clientSecret: clientSecret ? clientSecret.slice(0,4) + "..." : null, - sharepointFileUrl, - pbUrl, - pbEmail, - pbPassword: pbPassword ? "***" : null -}); // ----------------------------- // LOGGING // ----------------------------- @@ -52,7 +35,7 @@ function log(message) { } // ----------------------------- -// HEADER + ROW DETECTION HELPERS +// HEADER DETECTOR // ----------------------------- //Gets the last column letter in the table converting from 0 index to excel actual column in letter form. function columnLetter(index) { @@ -64,30 +47,8 @@ function columnLetter(index) { return letter; } -const headerRowIndex = HEADER_ROW_INDEX + 1; - -function getColumnLetterForHeader(headerName, sheet, headerRowIndex) { - const headerRow = XLSX.utils.sheet_to_json(sheet, { header: 1, range: `${headerRowIndex}:${headerRowIndex}` })[0] - .map(h => (h || "").toString().trim().toLowerCase()); - const idx = headerRow.findIndex(h => h === headerName.toLowerCase()); - if (idx === -1) { - log(`❌ Header "${headerName}" not found. Row contents: ${JSON.stringify(headerRow)}`); - throw new Error(`Header "${headerName}" not found on row ${headerRowIndex}`); - } - log(`✅ Header "${headerName}" found at column index ${idx}`); - return columnLetter(idx); -} -function getNotesColumnLetterFromParsedHeaders(headerMap) { - const headers = Object.keys(headerMap); - const notesIndex = headers.findIndex(h => h.trim().toLowerCase() === "notes"); - if (notesIndex === -1) { - log(`❌ 'Notes' header not found in HEADER_MAP keys: ${headers.join(", ")}`); - throw new Error("Notes column not found"); - } - return columnLetter(notesIndex); -} // ----------------------------- -// GRAPH TOKEN +// GRAPH TOKEN ACQUISITION // ----------------------------- async function getGraphToken() { const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`; @@ -117,22 +78,7 @@ async function downloadExcelFromGraph(graphToken, driveId, itemId) { } // ----------------------------- -// GRAPH UPLOADER HELPER -// ----------------------------- -async function uploadExcelToGraph(graphToken, driveId, itemId, buffer) { - const url = `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}/content`; - const res = await axios.put(url, buffer, { - headers: { - Authorization: `Bearer ${graphToken}`, - "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - } - }); - log(`✅ Excel file updated in SharePoint: ${res.status} ${res.statusText}`); -} - - -// ----------------------------- -// POCKETBASE LOGIN +// POCKETBASE LOGIN AND TOKEN ACQUISITION // ----------------------------- async function getPocketBaseClient() { const pb = new PocketBase(pbUrl); @@ -141,215 +87,198 @@ async function getPocketBaseClient() { return pb; } -function parseExcelFile(input, headerMap) { +// ----------------------------- +// Get Data From Excel File +// ----------------------------- + +const HEADER_ROW_INDEX = 2; // Row 3 in Excel +const DATA_START_ROW_INDEX = 3; // Row 4 in Excel → first data row + +//clean and coerce Excel values to match PocketBase schema expectations +function cleanString(val) { + if (val == null) return ""; + return String(val) + .replace(/\u00A0/g, " ") // normalize non-breaking spaces + .replace(/[\r\n\t]/g, " ") // strip control chars + .replace(/\s+/g, " ") // collapse whitespace + .trim(); +} + +function normalizeForPB(val, type) { + const cleaned = cleanString(val); + + // Treat common placeholders as empty + const emptyTokens = ["n/a", "na", "null", "none"]; + if (emptyTokens.includes(cleaned.toLowerCase())) { + switch (type) { + case "number": return 0; // PocketBase defaults to 0 + case "bool": return null; + case "string": return null; + case "email": return null; // email empty + case "json": return null; + case "date": return null; // date empty + default: return null; + } + } + + if (cleaned === "") { + switch (type) { + case "number": return 0; + case "bool": return null; + case "string": return null; + case "email": return null; + case "json": return null; + case "date": return null; + default: return null; + } + } + + switch (type) { + case "bool": + case "boolean": { + const lower = cleaned.toLowerCase(); + if (["true","yes","y","1"].includes(lower)) return true; // boolean true + if (["false","no","n","0"].includes(lower)) return false; // boolean false + return null; + } + case "number": { + const num = Number(cleaned); + return Number.isNaN(num) ? 0 : num; + } + case "date": { + const d = new Date(cleaned); + return isNaN(d.getTime()) ? null : d.toISOString(); + } + case "string": + default: + return cleaned; + } +} + +export function parseExcelFile(input) { let data; - // In case of data from graph if (Buffer.isBuffer(input)) { data = input; - log(`Reading Excel file from Graph buffer`); - // In case of file path (Not used at this time) + log("Reading Excel file from Graph buffer"); } else { data = fs.readFileSync(input); log(`Reading Excel file: ${input}`); } const workbook = XLSX.read(data, { type: "buffer", cellDates: true }); - const sheet = workbook.Sheets["Job Sheet"]; // explicitly use Job Sheet - const range = XLSX.utils.decode_range(sheet["!ref"]); + const sheet = workbook.Sheets["Job Sheet"]; + if (!sheet) throw new Error('Sheet named "Job Sheet" not found'); + + const range = XLSX.utils.decode_range(sheet["!ref"]); const merges = sheet["!merges"] || []; + // Helper to get value from cell or merged region const getValue = (r, c) => { const addr = XLSX.utils.encode_cell({ r, c }); const cell = sheet[addr]; if (cell) return cell.v ?? cell.w ?? null; + for (const m of merges) { if (m.s.r <= r && r <= m.e.r && m.s.c <= c && c <= m.e.c) { - const master = sheet[XLSX.utils.encode_cell({ r: m.s.r, c: m.s.c })]; + const master = sheet[XLSX.utils.encode_cell(m.s)]; return master ? (master.v ?? master.w ?? null) : null; } } return null; }; - // Read headers + // 1. Read headers (Row 3) const rawHeaders = []; for (let c = range.s.c; c <= range.e.c; c++) { - let h = getValue(HEADER_ROW_INDEX, c); - rawHeaders.push(h ? String(h).trim() : ""); + const val = getValue(HEADER_ROW_INDEX, c); + rawHeaders.push(val ? String(val).trim() : ""); } - log(`Found ${rawHeaders.length} headers in Row 3`); - log(`Headers: ${rawHeaders.join(" | ")}`); - // Compare Excel headers against HEADER_MAP - const mappedHeaders = []; - const unmappedHeaders = []; - for (const h of rawHeaders) { - if (headerMap[h]) { - mappedHeaders.push([h, headerMap[h]]); - } else { - unmappedHeaders.push([h, ""]); + + // 2. Build lookup map once + const excelHeaderToConfig = Object.values(FIELD_CONFIG).reduce((map, cfg) => { + if (cfg.excelHeader) { + const key = cfg.excelHeader.trim(); + map[key] = cfg; + map[key.toLowerCase()] = cfg; } - } + return map; + }, {}); -// Output as CSV-style rows -console.log("\n📑 Header mapping preview (CSV style):"); -console.log("ExcelHeader,PocketBaseField"); -for (const [excel, pb] of mappedHeaders) { - console.log(`${excel},${pb}`); -} -for (const [excel, pb] of unmappedHeaders) { - console.log(`${excel},${pb}`); -} + // 3. Rich header mapping + const headerMapping = rawHeaders.map(raw => { + const clean = raw.trim(); + const config = excelHeaderToConfig[clean] || excelHeaderToConfig[clean.toLowerCase()]; - // Parse data rows + return config + ? { original: clean, pbField: config.pbField, type: config.type, isMapped: true, config } + : { original: clean, pbField: "", type: "string", isMapped: false, isUnknown: true }; + }); + + log(`Mapped ${headerMapping.filter(h => h.isMapped).length} columns`); + + // 4. Parse data rows (starting Row 4) const rows = []; for (let r = DATA_START_ROW_INDEX; r <= range.e.r; r++) { - const row = {}; + const values = []; let hasData = false; + for (let c = range.s.c; c <= range.e.c; c++) { - const excelHeader = rawHeaders[c - range.s.c]; - const pbField = headerMap[excelHeader]; - if (!pbField) continue; - let val = getValue(r, c); - if (val !== null && val !== undefined) { - const s = String(val).trim(); - if (s === "" || /^n\/?a$/i.test(s) || s === "#N/A") val = null; - } - row[pbField] = val; + const val = getValue(r, c); + values.push(val ?? null); if (val !== null && val !== "") hasData = true; } - if (hasData) rows.push(row); + if (!hasData) continue; + + const rowObj = {}; + headerMapping.forEach((map, i) => { + if (map.isMapped) { + rowObj[map.pbField] = normalizeForPB(values[i], map.type); + } + }); + + rows.push(rowObj); } - log(`Loaded ${rows.length} real jobs`); - if (rows[0]) { - log(`First job preview: Job_Number=${rows[0].Job_Number}, pb_id=${rows[0].id}`); - - } - return rows; + return { + data: rows, // parsed row objects + headerMapping, // rich info about headers + rawHeaders, // original headers for debugging + totalRows: rows.length // count of parsed rows + }; } -// ----------------------------- -// HEADER MAP -// ----------------------------- -const HEADER_MAP = { - "Job_Number": "Job_Number", - "Job_Full_Name": "Job_Full_Name", - "Job_Name": "Job_Name", - "Job Address": "Job_Address", - "Job Type": "Job_Type", - "Flag": "Flag", - "Tax_Exempt": "Tax_Exempt", - "Job Status": "Job_Status", - "Job Division": "Job_Division", - "Estimator": "Estimator", - "Office Rep": "Office_Rep", - "Manager": "Project_Manager", - "Company/Client": "Company_Client", - "Contact Person": "Contact_Person", - "Phone Number": "Phone_Number", - "Email": "Email", - "Due Date": "Due_Date", - "Due Date Source": "Due_Date_Source", - "Due Date Counter": "Due_Date_Counter", - "Due Time": "Due_Time", - "Tax_Exempt_Docs_Uploaded": "Tax_Exempt_Docs_Uploaded", - "Docs Uploaded": "Docs_Uploaded", - "QB Created": "QB_Created", - "Added to Calendar": "Added_To_Calendar", - "PSwift Uploaded": "PSwift_Uploaded", - "Voxer Created": "Voxer_Created", - "Estimate Reviewed": "Estimate_Reviewed", - "Estimate Draft": "Estimate_Draft", - "Estimate Approved": "Estimate_Approved", - "Start Date": "Start_Date", - "Schedule_Confidence": "Schedule_Confidence", - "Notes": "Notes", - "Job QB Link": "Job_QB_Link", - "Voxer Link": "Voxer_Link", - "Job_Codes": "Job_Codes", - "Submission Date": "Submission_Date", - "Active": "Active", - "Job Folder Link": "Job_Folder_Link", - "pb_id": "id" -}; // ----------------------------- // MAIN // ----------------------------- let parsedRows = []; +let rawHeaders = []; (async () => { try { log("🔵 Starting Excel Sync…"); - const graphToken = await getGraphToken(); const pb = await getPocketBaseClient(); // These can be update if the Excel File changes const driveId = "b!9YOqqr2xM0G2DFBwMEJYY4UzQgocFddEtpLYWuS9_AtkCKl2q8yjQJteQ7Ti4QSx"; const itemId = "01SPNXLDSQTMG4F6BDANCLJU2FZKEDQLVU"; const excelBuffer = await downloadExcelFromGraph(graphToken, driveId, itemId); - parsedRows = parseExcelFile(excelBuffer, HEADER_MAP); - - // Run enhanced test (safe, no writes) - runEnhancedTest(parsedRows, graphToken, pb); - // At this stage you have: - // - graphToken (for SharePoint updates later) - // - pb client (for PB updates) - // - rows[] mapped to PB fields - - const updatedBuffer = exportExcelforDL(parsedRows); - await updateNotesInSharePoint(graphToken, driveId, itemId, "Job Sheet", parsedRows, pb, excelBuffer); - // Sync to PocketBase - //await syncRowsToPocketBase(parsedRows, pb); - + const parsed = parseExcelFile(excelBuffer); + parsedRows = parsed.data; + rawHeaders = parsed.rawHeaders; + log(rawHeaders) log("✅ First part complete: tokens + Excel parsing ready"); } catch (err) { log("❌ Sync failed: " + (err.message || err)); process.exit(1); } })(); -// ----------------------------- -// ENHANCED TEST HARNESS -// ----------------------------- -function runEnhancedTest(rows, graphToken, pb) { - log("🔎 Running enhanced test (no writes)…"); - - // 1. Verify tokens - log(`Graph token length: ${graphToken.length}`); - log(`PocketBase token length: ${pb.authStore.token.length}`); - - // 2. Verify Excel parsing - log(`Parsed ${rows.length} rows from Excel`); - if (rows.length > 0) { - const sample = rows[0]; - log("Sample row preview:"); - log(` Job_Number: ${sample.Job_Number}`); - log(` Job_Full_Name: ${sample.Job_Full_Name}`); - log(` Active: ${sample.Active}`); - log(` pb_id: ${sample.id}`); - log(` Notes: ${sample.Notes}`); - } - - // 3. Verify header mapping - const mappedFields = Object.values(HEADER_MAP); - const sampleKeys = Object.keys(rows[0] || {}); - const missingFields = mappedFields.filter(f => !sampleKeys.includes(f)); - if (missingFields.length > 0) { - log(`⚠️ Missing mapped fields in sample row: ${missingFields.join(", ")}`); - } else { - log("✅ All mapped fields present in sample row"); - } - - log("✅ Enhanced test complete — no writes performed"); -} - - // ------------------------- // Export to xlsx - Excel // --------------------------- // Toggle this flag to enable/disable the export server -const ENABLE_EXPORT = false; // set to false to bypass download +const ENABLE_EXPORT = true; // set to false to bypass download function exportExcelforDL(parsedRows) { if (!parsedRows || parsedRows.length === 0) { @@ -424,90 +353,89 @@ if (ENABLE_EXPORT) { // ----------------------------- // UPDATE NOTES CELLS IN SHAREPOINT (REAL-TIME) // ----------------------------- -function getNotesColumnLetterFromParsedHeaders(headerMap) { - const headers = Object.keys(headerMap); - const notesIndex = headers.findIndex(h => h.trim().toLowerCase() === "notes"); - if (notesIndex === -1) { - log(`❌ 'Notes' header not found in HEADER_MAP keys: ${headers.join(", ")}`); - throw new Error("Notes column not found"); - } - return columnLetter(notesIndex); -} - +//function getNotesColumnLetterFromParsedHeaders(rawHeaders) { +// const notesIndex = rawHeaders.findIndex(h => h.trim().toLowerCase() === "notes"); +// if (notesIndex === -1) { +// log(`❌ 'Notes' header not found in HEADER_MAP keys: ${rawHeaders.join(", ")}`); +// throw new Error("Notes column not found"); +// } +// return columnLetter(notesIndex); +//} +//await updateNotesInSharePoint(graphToken, driveId, itemId, "Job Sheet", parsedRows, pb, excelBuffer); // Update only changed Notes cells using workbook session + PATCH. // This version relies solely on DATA_START_ROW_INDEX for row mapping. -async function updateNotesInSharePoint(graphToken, driveId, itemId, worksheetName, parsedRows, pb, workbookBuffer) { - const wb = XLSX.read(workbookBuffer, { type: "buffer", cellDates: true }); - const sheet = wb.Sheets[worksheetName]; - if (!sheet) throw new Error(`Worksheet "${worksheetName}" not found`); +//async function updateNotesInSharePoint(graphToken, driveId, itemId, worksheetName, parsedRows, pb, workbookBuffer) { +// const wb = XLSX.read(workbookBuffer, { type: "buffer", cellDates: true }); +// const sheet = wb.Sheets[worksheetName]; +// if (!sheet) throw new Error(`Worksheet "${worksheetName}" not found`); // Column detection aligned to HEADER_MAP (same mapping used by parseExcelFile) - const notesColumnLetter = getNotesColumnLetterFromParsedHeaders(HEADER_MAP); - log(`✅ Notes column resolved to ${notesColumnLetter} via HEADER_MAP`); + //const notesColumnLetter = getNotesColumnLetterFromParsedHeaders(rawHeaders); + //og(`✅ Notes column resolved to ${notesColumnLetter} via rawHeaders`); // Create a workbook session that persists changes - const sessionRes = await axios.post( - `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}/workbook/createSession`, - { persistChanges: true }, - { headers: { Authorization: `Bearer ${graphToken}` } } - ); - const sessionId = sessionRes.data.id; + //const sessionRes = await axios.post( + // `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}/workbook/createSession`, + // { persistChanges: true }, + // { headers: { Authorization: `Bearer ${graphToken}` } } + // ); + // const sessionId = sessionRes.data.id; - let changes = 0; + // let changes = 0; // Loop parsed rows; map i to Excel row using DATA_START_ROW_INDEX (zero-based) - for (let i = 0; i < parsedRows.length; i++) { - const row = parsedRows[i]; - const jobNumber = row.Job_Number; - if (jobNumber == null || jobNumber === "") continue; +// for (let i = 0; i < parsedRows.length; i++) { +// const row = parsedRows[i]; +// const jobNumber = row.Job_Number; +// if (jobNumber == null || jobNumber === "") continue; - try { - const records = await pb.collection(pbCollection).getFullList({ - filter: Number.isFinite(jobNumber) ? `Job_Number=${jobNumber}` : `Job_Number="${jobNumber}"`, - batchSize: 1 - }); - if (records.length === 0) continue; +// try { +// const records = await pb.collection(pbCollection).getFullList({ +// filter: Number.isFinite(jobNumber) ? `Job_Number=${jobNumber}` : `Job_Number="${jobNumber}"`, +// batchSize: 1 +// }); +// if (records.length === 0) continue; // Normalize notes for fair comparison - const normalize = v => (v ?? "").toString().replace(/\s+/g, " ").trim(); - const pbNotes = normalize(records[0].Notes); - const excelNotes = normalize(row.Notes); +// const normalize = v => (v ?? "").toString().replace(/\s+/g, " ").trim(); +// const pbNotes = normalize(records[0].Notes); +// const excelNotes = normalize(row.Notes); - if (pbNotes !== excelNotes) { + // if (pbNotes !== excelNotes) { // Excel uses 1-based row numbers; DATA_START_ROW_INDEX is zero-based - const excelRowNumber = DATA_START_ROW_INDEX + i + 1; - const cellAddress = `${notesColumnLetter}${excelRowNumber}`; +// const excelRowNumber = DATA_START_ROW_INDEX + i + 1; +// const cellAddress = `${notesColumnLetter}${excelRowNumber}`; - log(`✏️ Notes change for Job_Number=${jobNumber}`); - log(` Before: "${excelNotes}"`); - log(` After : "${pbNotes}"`); - log(`➡️ Writing to ${worksheetName}!${cellAddress}`); +// log(`✏️ Notes change for Job_Number=${jobNumber}`); +// log(` Before: "${excelNotes}"`); +// log(` After : "${pbNotes}"`); +// log(`➡️ Writing to ${worksheetName}!${cellAddress}`); - await axios.patch( - `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}/workbook/worksheets('${worksheetName}')/range(address='${cellAddress}')`, - { values: [[pbNotes]] }, - { - headers: { - Authorization: `Bearer ${graphToken}`, - "Content-Type": "application/json", - "workbook-session-id": sessionId - } - } - ); +// await axios.patch( +// `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}/workbook/worksheets('${worksheetName}')/range(address='${cellAddress}')`, +// { values: [[pbNotes]] }, +// { +// headers: { +// Authorization: `Bearer ${graphToken}`, +// "Content-Type": "application/json", +// "workbook-session-id": sessionId +// } +// } +// ); - row.Notes = pbNotes; - changes++; - } - } catch (err) { - log(`❌ Failed for Job_Number=${jobNumber}: ${err.message}`); - } - } +// row.Notes = pbNotes; +// changes++; +// } +// } catch (err) { +// log(`❌ Failed for Job_Number=${jobNumber}: ${err.message}`); +// } +// } - await axios.post( - `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}/workbook/closeSession`, - {}, - { headers: { Authorization: `Bearer ${graphToken}`, "workbook-session-id": sessionId } } - ); +// await axios.post( +// `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}/workbook/closeSession`, +// {}, +// { headers: { Authorization: `Bearer ${graphToken}`, "workbook-session-id": sessionId } } +// ); - log(`✅ Notes-only sync complete. Changes applied: ${changes}`); -} \ No newline at end of file +// log(`✅ Notes-only sync complete. Changes applied: ${changes}`); +//} \ No newline at end of file diff --git a/backend/src/excelsync.log b/backend/src/excelsync.log deleted file mode 100644 index e69de29..0000000 diff --git a/backend/utilities/sharepointShareLinktoGraph.html b/backend/utilities/sharepointShareLinktoGraph.html index 076350d..b56bec4 100644 --- a/backend/utilities/sharepointShareLinktoGraph.html +++ b/backend/utilities/sharepointShareLinktoGraph.html @@ -2,6 +2,7 @@ + SharePoint Link Encoder