Step 1: Open a new Google Sheet.
Step 2: Click
Extensions → Apps Script.
Step 3: Delete any existing code and paste the Apps Script code shown below.
Step 4: Click
Deploy → New deployment → Web app. Set "Who has access" to
Anyone. Click Deploy and copy the URL.
Step 5: Paste the URL into the box above and click "Push Now".
⚠️ Existing users: If you already have an Apps Script deployed, replace the code with the version below and
Deploy → Manage deployments → update to latest version. This unlocks multi-user live sync.
Apps Script code to paste:
function doPost(e) {
try {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var body = JSON.parse(e.postData.contents);
// ── PULL REQUEST: return all jobs stored in _Sync sheet ──
if (body && body.action === 'get') {
var syncSheet = ss.getSheetByName('_Sync');
if (!syncSheet) return ContentService.createTextOutput('[]').setMimeType(ContentService.MimeType.TEXT);
var val = syncSheet.getRange('A1').getValue();
return ContentService.createTextOutput(val || '[]').setMimeType(ContentService.MimeType.TEXT);
}
// ── PUSH REQUEST: body is the full jobs array ──
var data = Array.isArray(body) ? body : null;
if (!data) return ContentService.createTextOutput('ERROR: expected array').setMimeType(ContentService.MimeType.TEXT);
// Write human-readable Jobs sheet (visible in Google Sheets)
var sheet = ss.getSheetByName('Jobs') || ss.insertSheet('Jobs');
sheet.clearContents();
var headers = ['ID','Job Ref','Customer','Address','Postcode','Install Date',
'Job Price','Labour','Materials','Profit/Unit','Total Profit',
'Units','Type','Saved At','Invoiced At','Engineer','Email','Phone'];
sheet.appendRow(headers);
data.forEach(function(j) {
sheet.appendRow([
j.id, j.jobRef||'', j.custName||'', j.custAddr||'', j.custPostcode||'',
j.instDate||'', j.jobPrice||0, j.labour||0, j.totalMaterials||0,
j.perUnit||0, (j.perUnit||0)*(j.numUnits||1),
j.numUnits||1, j.jobType||'quote', j.savedAt||'', j.invoicedAt||'',
j.engineer||'', j.custEmail||'', j.custPhone||''
]);
});
// Store full JSON in _Sync sheet (read by all users on load / every 30s)
var syncSheet = ss.getSheetByName('_Sync') || ss.insertSheet('_Sync');
syncSheet.getRange('A1').setValue(JSON.stringify(data));
syncSheet.getRange('B1').setValue(new Date().toISOString());
syncSheet.getRange('C1').setValue(data.length + ' jobs');
return ContentService.createTextOutput('OK').setMimeType(ContentService.MimeType.TEXT);
} catch(err) {
return ContentService.createTextOutput('ERROR: ' + err.toString()).setMimeType(ContentService.MimeType.TEXT);
}
}
function doGet(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sync = ss.getSheetByName('_Sync');
var ts = sync ? sync.getRange('B1').getValue() : 'never';
var cnt = sync ? sync.getRange('C1').getValue() : '0 jobs';
return ContentService.createTextOutput('Prestige Air sync OK — ' + cnt + ' — last push: ' + ts).setMimeType(ContentService.MimeType.TEXT);
}