PT Seva Food Order Backend Script✅

// 🔥 PT SEVA MASTER ENGINE v5.26 (Pro Backend with UPI/COD & Zomato-Level Metrics) // 🎯 RULES: 7km Order Limit | 4km Driver Allocation | 20km Registration Limit // 📝 Sheet ID: 1NjqHSgvmcD6Oo7Xd6VUcIRLG-5P57x6WQkoTCveUeqg const SHEET_ID = "1NjqHSgvmcD6Oo7Xd6VUcIRLG-5P57x6WQkoTCveUeqg"; const MAX_DISTANCE_KM = 7; // 🔥 Customer Order Limit const DRIVER_ALLOC_KM = 4; // 🔥 Driver Allocation Limit const REGISTRATION_KM = 20; // 🔥 Driver Registration Limit const BASE_FEE = 20; const EXTRA_PER_KM = 5; function doGet(e) { return handleRequest(e); } function doPost(e) { return handleRequest(e); } function handleRequest(e) { var lock = LockService.getScriptLock(); if (!lock.tryLock(10000)) return response({status: "FAIL", msg: "Server Busy"}); try { if (!e.parameter.action) return response({status: "FAIL", msg: "No Action"}); var op = e.parameter.action; var ss = SpreadsheetApp.openById(SHEET_ID); // ================================================= // 🍔 1. COMMON & FOODIE LOGIC // ================================================= if (op == "getCities") { var sheet = ss.getSheetByName("Restaurants"); var data = sheet.getDataRange().getValues(); var cities = []; for (var i = 1; i < data.length; i++) { var city = String(data[i][8]).trim(); var status = String(data[i][10]).toUpperCase(); if (city && status == "ACTIVE" && cities.indexOf(city) == -1) cities.push(city); } return response({status: "SUCCESS", cities: cities}); } if (op == "getNearestCity") { var uLat = Number(e.parameter.lat); var uLng = Number(e.parameter.lng); var sheet = ss.getSheetByName("Restaurants"); var data = sheet.getDataRange().getValues(); var closestCity = ""; var minDst = REGISTRATION_KM; for (var i = 1; i < data.length; i++) { var status = String(data[i][10]).toUpperCase(); if (status == "ACTIVE") { var rLat = data[i][12]; var rLng = data[i][13]; var rCity = String(data[i][8]).trim(); if(rLat && rLng) { var dst = getDist(uLat, uLng, rLat, rLng); if (dst < minDst) { minDst = dst; closestCity = rCity; } } } } if (closestCity !== "") return response({status: "SUCCESS", city: closestCity}); else return response({status: "FAIL", msg: "No active city in 20km range"}); } if (op == "getRestaurants") { var sheet = ss.getSheetByName("Restaurants"); var data = sheet.getDataRange().getValues(); var reqCity = e.parameter.city; var list = []; for (var i = 1; i < data.length; i++) { var status = String(data[i][10]).toUpperCase(); var rCity = String(data[i][8]).trim(); var storeToggle = String(data[i][14]).toUpperCase(); if (status == "ACTIVE" && storeToggle == "OPEN") { if (!reqCity || (reqCity && rCity.toLowerCase() == String(reqCity).toLowerCase())) { var realID = String(data[i][1]); var shopName = String(data[i][3]).replace(/\s+/g, ''); var maskedID = shopName + "-" + realID.slice(-4); list.push({ id: realID, display_id: maskedID, name: data[i][3], cuisine: data[i][4], image: data[i][11], city: rCity, lat: data[i][12], lng: data[i][13], rating: data[i][16] || "4.0" }); } } } return response({status: "SUCCESS", data: list}); } if (op == "getMenu") { var sheet = ss.getSheetByName("Menu"); var data = sheet.getDataRange().getValues(); var list = []; for(var i=1; i MAX_DISTANCE_KM) return response({status: "FAIL", msg: "Too far ("+dist.toFixed(1)+"km). Max "+MAX_DISTANCE_KM+"km allowed."}); if (dist > 3) deliveryFee = BASE_FEE + Math.round((dist - 3) * EXTRA_PER_KM); if(deliveryFee > 100) deliveryFee = 100; } var grandTotal = itemTotal + deliveryFee; var payMode = e.parameter.payMode || "COD"; var uData = uSheet.getDataRange().getValues(); var userRow = -1; for(var i=1; i=1; i--) { if(String(data[i][2]) == String(phone) || String(data[i][1]) == String(e.parameter.oid)) { list.push({oid: data[i][1], status: data[i][13], driver: data[i][6] || "Assigning..."}); } } return response({status:"SUCCESS", data: list}); } // ================================================= // 🏪 2. RESTAURANT LOGIC // ================================================= if (op == "registerRestaurant") { var sheet = ss.getSheetByName("Restaurants"); var data = sheet.getDataRange().getValues(); var newPhone = String(e.parameter.phone).trim(); for(var i=1; i=1; i--) { if(String(oData[i][3]) == String(uid) || String(oData[i][4]) == String(uid)) { var status = oData[i][13]; var amt = Number(oData[i][8]); var driverID = String(oData[i][6]); var orderTime = new Date(oData[i][0]); var isToday = (orderTime.getDate() === now.getDate() && orderTime.getMonth() === now.getMonth() && orderTime.getFullYear() === now.getFullYear()); if(status == "DELIVERED" && isToday) sale += amt; if(status == "REJECTED" && (now - orderTime) > 30 * 60 * 1000) continue; if( (status !== "DELIVERED" && status !== "REJECTED") || (status === "REJECTED" && (now - orderTime) <= 30 * 60 * 1000) || (status === "DELIVERED" && isToday) ) { var msgTxt = "Finding Driver..."; var drvName = ""; if(driverID) { for(var d=1; d= 1; a--) { if(String(attData[a][1]) == String(e.parameter.userid) && attData[a][4] == "") { attSheet.getRange(a+1, 5).setValue(new Date().toLocaleTimeString()); break; } } break; } } return response({status:"SUCCESS"}); } if (op == "getDeliveryTasks") { var oSheet = ss.getSheetByName("Orders"); var dSheet = ss.getSheetByName("DeliveryPartners"); var rSheet = ss.getSheetByName("Restaurants"); var oData = oSheet.getDataRange().getValues(); var dData = dSheet.getDataRange().getValues(); var rData = rSheet.getDataRange().getValues(); var restMap = {}; for(var r=1; r=1; i--) { var orderDate = new Date(oData[i][0]); var isToday = (orderDate.getDate() === todayDate.getDate() && orderDate.getMonth() === todayDate.getMonth() && orderDate.getFullYear() === todayDate.getFullYear()); var st = oData[i][13], drv = oData[i][6], city = oData[i][11], payMode = String(oData[i][12]).toUpperCase(); var restMobile = oData[i][3], restId = oData[i][4], restName = oData[i][5] || "Restaurant"; var addr = oData[i][10], totalAmt = oData[i][8], deliveryFee = oData[i][9], lat = oData[i][16], lng = oData[i][17]; if(String(drv) == String(drvRealID)) { if(st != "DELIVERED") { myOrders.push({ oid: oData[i][1], rest: restName, restPhone: restMobile, addr: addr, amt: totalAmt, fee: deliveryFee, status: st, lat: lat, lng: lng, payMode: payMode }); } else if (st == "DELIVERED" && isToday) { todayEarn += Number(deliveryFee); myOrders.push({ oid: oData[i][1], rest: restName, restPhone: restMobile, addr: addr, amt: deliveryFee, status: st, lat: lat, lng: lng, payMode: payMode }); } } else if(st == "READY" && drv == "") { var rLoc = restMap[String(restId)]; if (dLat && dLng && rLoc) { var distToRest = getDist(dLat, dLng, rLoc.lat, rLoc.lng); if (distToRest <= DRIVER_ALLOC_KM) { newOrders.push({ oid: oData[i][1], rest: restName, restPhone: restMobile, addr: addr, amt: totalAmt, fee: deliveryFee, status: st, lat: lat, lng: lng, payMode: payMode }); } } else if(String(city).trim().toLowerCase() == String(drvCity).trim().toLowerCase()) { newOrders.push({ oid: oData[i][1], rest: restName, restPhone: restMobile, addr: addr, amt: totalAmt, fee: deliveryFee, status: st, lat: lat, lng: lng, payMode: payMode }); } } } return response({status:"SUCCESS", new:newOrders, my:myOrders, stats:{wallet:wallet, cash:cash, todayEarn:todayEarn}}); } if (op == "acceptOrder") { var s = ss.getSheetByName("Orders"); var d = s.getDataRange().getValues(); for(var i=1; i= BATCH_SIZE) break; var accStatus = String(data[i][11] || data[i][10]).toUpperCase(); var email = String(data[i][config.emailCol - 1]).trim(); var billStatus = String(data[i][config.statusCol - 1]).trim(); var entityId = String(data[i][config.idCol - 1]).trim(); if (accStatus === "ACTIVE" && email !== "" && billStatus !== sentMark && entityId !== "") { billTemplate.getRange("B2").setValue(entityId); billTemplate.getRange("B3").setValue(period.start); billTemplate.getRange("B4").setValue(period.end); SpreadsheetApp.flush(); var pdfBlob = generatePDF(ss, billTemplate, config.prefix + "_" + entityId); var subject = "PT Seva Weekly Payout - " + entityId + " (" + period.label + ")"; var body = "Namaste,\n\nApka weekly payout bill (" + period.start.toLocaleDateString() + " se " + period.end.toLocaleDateString() + ") attach kar diya gaya hai.\n\nTeam PT Seva"; try { GmailApp.sendEmail(email, subject, body, { from: SENDER_EMAIL, attachments: [pdfBlob], name: "PT Seva Payouts" }); sheet.getRange(i + 1, config.statusCol).setValue(sentMark); processedCount++; } catch (e) { sheet.getRange(i + 1, config.statusCol).setValue("ERR: " + e.message); } } } } function getPayoutPeriod(today) { var d = today.getDate(); var start = new Date(today); var end = new Date(today); var label = d + "-" + (today.getMonth() + 1); if (d === 8) { start.setDate(1); end.setDate(7); } else if (d === 15) { start.setDate(8); end.setDate(14); } else if (d === 22) { start.setDate(15); end.setDate(21); } else if (d === 1) { start = new Date(today.getFullYear(), today.getMonth() - 1, 22); end = new Date(today.getFullYear(), today.getMonth(), 0); } return { start: start, end: end, label: label }; } function generatePDF(ss, sheet, fileName) { var url = "https://docs.google.com/spreadsheets/d/" + ss.getId() + "/export?exportFormat=pdf&format=pdf&gid=" + sheet.getSheetId() + "&size=A4&portrait=true&fitw=true&gridlines=false"; var token = ScriptApp.getOAuthToken(); var response = UrlFetchApp.fetch(url, { headers: { 'Authorization': 'Bearer ' + token } }); return response.getBlob().setName(fileName + ".pdf"); } function setupFinalTriggers() { var triggers = ScriptApp.getProjectTriggers(); triggers.forEach(t => { if(t.getHandlerFunction() === "processAllPayouts") ScriptApp.deleteTrigger(t); }); ScriptApp.newTrigger("processAllPayouts").timeBased().everyHours(1).create(); } // 🔥 FIXED: Archive Movement with full 26 Columns + 1 Archive Date function runArchive() { var ss = SpreadsheetApp.openById(SHEET_ID); var oSheet = ss.getSheetByName("Orders"); var aSheet = ss.getSheetByName("Archive"); var oData = oSheet.getDataRange().getValues(); var aData = aSheet.getDataRange().getValues(); var toDeleteOrders = []; var toDeleteArchive = []; var now = new Date(); for (var i = 1; i < oData.length; i++) { var orderDate = new Date(oData[i][0]); var isToday = (orderDate.getDate() === now.getDate() && orderDate.getMonth() === now.getMonth() && orderDate.getFullYear() === now.getFullYear()); if ((oData[i][13] === "DELIVERED" || oData[i][13] === "REJECTED") && !isToday) { var archivedRow = [ oData[i][0], oData[i][1], oData[i][2], oData[i][3], oData[i][4], oData[i][5], oData[i][6], oData[i][7], oData[i][8], oData[i][9], oData[i][10], oData[i][11], oData[i][12], oData[i][13], oData[i][14], oData[i][15], oData[i][16], oData[i][17], oData[i][18], oData[i][19] || "", oData[i][20] || 0, oData[i][21] || 0, oData[i][22] || "", oData[i][23] || "", oData[i][24] || "", oData[i][25] || "", now ]; aSheet.appendRow(archivedRow); toDeleteOrders.push(i+1); } } for (var j = toDeleteOrders.length - 1; j >= 0; j--) oSheet.deleteRow(toDeleteOrders[j]); for (var k = 1; k < aData.length; k++) { var archDate = new Date(aData[k][26]); if ((now - archDate) > 30 * 24 * 60 * 60 * 1000) { toDeleteArchive.push(k+1); } } for (var m = toDeleteArchive.length - 1; m >= 0; m--) aSheet.deleteRow(toDeleteArchive[m]); }