How to earn by selling true reviews

Earn Money with TECH X Ray: Full Guide

TECH X Ray: Honest Gadget Reviews Bhejein Aur Paise Kamayein!


Agar aapke paas Tech ki acchi knowledge hai, toh apna time waste mat karein. Hamare bot par detailed reviews share karein aur apne dedicated dashboard se live earnings bank me transfer karein.

Work From Home: Kitna Time Aur Kitna Paisa?
  • Time Required: Ek genuine review ki research aur typing karne me aapko lagbhag 20 se 30 minute ka time lagega.
  • Base Price (₹10): Shuruat me aapko har ek approved final review ka Base Price ₹10 milega.
  • Quality Bonus: Agar aap zyada mehnat karke ekdum premium aur detailed review bhejte hain, toh hamari team aapka per post price ₹15 se ₹20 tak badha sakti hai.
  • Loyalty Reward: Jo users hamare sath purane hain aur lagatar (consistent) achhe reviews bhej rahe hain, unka "Rate Per Post" unki profile ke according automatic badha diya jata hai.
100% Work From Home using Mobile/Laptop
Step 1: Ek Perfect Review Ke Liye Research Kaise Karein?

1. E-Commerce Sites Par Jayein

Kisi bhi kam se kam 1 mahine purane phone ya tech product ko chunein. Flipkart ya Amazon par jaakar khud uski specifications aur wahan logo dwara diye gaye reviews padhein.

2. YouTube Videos Aur Comments Dekhein

YouTube par us product ke reviews dekhein. Sabse zaroori kaam: Logo ke comments dhyaan se padhein ki aam janta ko us product me kya dikkat aa rahi hai ya kya pasand aa raha hai.

3. Plus (+) aur Minus (-) Analyze Karein

Ab dekhein ki us gadget me achhi baatein kitni hain aur buraiyan kitni hain. Kaun se points zyada bhari hain? Iska ek sachha nichod nikaalein.

Step 2: Hamare Bot Par Review Is Format Me Bhejein
Best Example
Aise Bhejein Apna Final Message
👉 Product Name: Vivo T5 5G
💰 Approx Price: ₹17,999

Key Highlights:

🔹 Design & Build: Phone dikhne me slim hai aur matte plastic back premium feel deta hai, fingerprints nahi lagte.
🔹 Display Quality: 6.58-inch AMOLED 120Hz display. Colors punchy hain aur dhoop me clear dikhta hai.
🔹 Performance: Dimensity 7200 processor. Apps smoothly chalte hain aur BGMI medium settings par lag-free chalti hai.
🔹 Camera / Extra: 50MP main camera din me best photos leta hai, par raat ke time photo quality average lagi.
🔹 Battery Life: 5000mAh battery 1.5 din chal jati hai. 44W charger isko 1 ghante me full charge karta hai.
✅ Achhaiyan (Pros):
  • Display aur colors bahut shaandar hain.
  • Battery backup aur fast charging best hai.
❌ Hame kya pasand nahi aaya (Cons):
  • Ultra-wide camera nahi diya gaya hai.
  • Pehle se bahut saare faltu apps aate hain.
💡 Final Advice: Buy or Not?
Haan, is budget me hamari taraf se yeh ek best deal hai. Agar aapko achha display aur battery chahiye, toh paise waste nahi honge!
Step 3: Live Earnings Aur Fast Withdrawals

Review approve hote hi aapke dedicated dashboard me paise add ho jate hain. Yahan dekhein hamare users ki live earnings aur bank withdrawal proof:

Wallet Dashboard Balance

10 approved posts par ₹50.00 ka live balance.

Dashboard Earning Proof

Bank Withdrawal Success

₹50 aur ₹10 ke amounts instantly bank account me paid ho chuke hain.

Withdrawal Success Proof

STRICT WARNING: No AI & No Cheating!

Dhyan rakhein, AI (ChatGPT, Gemini) ka use karna ya kisi dusri website se review copy-paste karna sakht mana hai. AI se likhe gaye reviews genuine nahi lagte. Agar hamari checking team ne aapki aisi koi bhi galti pakdi, toh aapka Account hamesha ke liye Block (Ban) kar diya jayega aur bacha hua balance zero kar diya jayega.

Hum Itni Sakhti Kyu Kar Rahe Hain?

Kyunki hamare YouTube Channel par log hamare Final Reviews par Trust karte hain. Unhi ke bharose par channel chalta hai. Aapki di gayi jankari se kisi audience ka paisa barbaad nahi hona chahiye. Aapki Imandari hi aapki Earning hai!

Ready To Start Your Earning Journey?

Account banane ka koi jhanjhat nahi! Apni Telegram ID ka use karke seedhe login karein.

Worker Dashboard Screen Shot

 





How to Find UPI ID easily

How to Find Your UPI ID

How to Find Your UPI ID

Follow these simple steps to find your UPI ID (VPA) in your payment apps to receive your earnings.

PhonePe
  • Open the PhonePe app on your phone.
  • Click on your Profile Picture at the top left corner.
  • Scroll down and tap on "UPI Settings" or "UPI IDs".
  • You will see your active UPI ID listed there.
Example: 9876543210@ybl @ybl / @ibl
Google Pay (GPay)
  • Open the Google Pay app.
  • Tap on your Profile Photo in the top right corner.
  • Tap on "Bank Accounts" and select your active bank.
  • Your UPI ID will be displayed under "Manage UPI IDs".
Example: name123@okaxis @okaxis / @okicici
Paytm
  • Open the Paytm app.
  • Tap on your Profile Picture / Menu at the top left.
  • Your UPI ID is written exactly below your Name and Profile QR code.
  • Click "Copy" next to it to easily copy it.
Example: 9876543210@paytm @paytm
Important: Please ensure you enter the exact UPI ID without any spelling mistakes in your wallet. Wrong UPI IDs may result in failed payments.
I Got My UPI ID (Close Page)

Worker Dashboard Backend Script✅

var token = '8923324846:AAGsoQwXwP8HhNzrykRHg1QUDDSKMnv4hiY'; var sheetId = '1JYYWCWfFiK4QKRQfuZAbSCTfd1Fn5JPB2Bri2eXI3K4'; var adminChatId = '966116673'; function getPendingAmount(ss, tgId) { var reqSheet = ss.getSheetByName("Withdrawal_Requests"); if (!reqSheet) return 0; var reqData = reqSheet.getDataRange().getValues(); var pending = 0; for (var i = 1; i < reqData.length; i++) { if (reqData[i][1] == tgId && reqData[i][5] === "Pending") { pending += parseFloat(reqData[i][3]) || 0; } } return pending; } function getPendingReviewsCount(ss, tgId) { var logSheet = ss.getSheetByName("MessagesLog"); if (!logSheet) return 0; var data = logSheet.getDataRange().getValues(); var count = 0; for (var i = 1; i < data.length; i++) { if (data[i][2] == tgId && data[i][6] === "Pending") { count++; } } return count; } function getWithdrawHistory(ss, tgId) { var sheet = ss.getSheetByName("Withdrawal_Requests"); if (!sheet) return []; var data = sheet.getDataRange().getValues(); var history = []; for (var i = data.length - 1; i >= 1; i--) { if (data[i][1] == tgId) { history.push({ date: data[i][0], amount: data[i][3], status: data[i][5] }); } } return history; } function doPost(e) { try { var contents = JSON.parse(e.postData.contents); // 1. ADMIN ACTION VIA TELEGRAM if (contents.callback_query) { var callbackData = contents.callback_query.data; var messageId = contents.callback_query.message.message_id; if (callbackData.startsWith("approve_") || callbackData.startsWith("reject_")) { var parts = callbackData.split("_"); var action = parts[0]; var row = parseInt(parts[1]); var ss = SpreadsheetApp.openById(sheetId); var logSheet = ss.getSheetByName("MessagesLog"); var userId = logSheet.getRange(row, 3).getValue(); if (action === "approve") { logSheet.getRange(row, 7).setValue("Approved"); editMessageText(adminChatId, messageId, "✅ Approved by Admin"); sendReply(userId, "🎉 Badhai ho! Aapka review approve ho gaya hai aur paise wallet mein add ho gaye hain."); } else { logSheet.getRange(row, 7).setValue("Rejected"); editMessageText(adminChatId, messageId, "❌ Rejected by Admin"); sendReply(userId, "😔 Maaf kijiye, aapka review reject kar diya gaya hai. Kripya rules follow karke bhejein."); } answerCallbackQuery(contents.callback_query.id, "Status Updated!"); } return ContentService.createTextOutput("OK"); } // 2. USER SENDS REVIEW if (contents.message) { var chatID = contents.message.chat.id; var name = ((contents.message.from.first_name || "") + " " + (contents.message.from.last_name || "")).trim(); var text = contents.message.text || contents.message.caption || "Media without text"; var lowerText = text.trim().toLowerCase(); if (lowerText === "/start") { return sendReply(chatID, "Welcome to TECH X Ray! 🚀\n\nHame apna gadget review yahan bhejein. Approve hone par aapke wallet me paise add ho gaye hain.\n\nApna Password janne ke liye /myid type karein."); } if (lowerText === "/myid") { return sendReply(chatID, "Aapka Login ID/Password hai: " + chatID + "\n\nIs ID aur apne Mobile Number ka use karke hamari website par login karein."); } var ss = SpreadsheetApp.openById(sheetId); var logSheet = ss.getSheetByName("MessagesLog"); var date = new Date(); logSheet.appendRow([Utilities.formatDate(date, "GMT+05:30", "dd/MM/yyyy"), Utilities.formatDate(date, "GMT+05:30", "HH:mm:ss"), chatID, name, "", text, "Pending"]); var lastRow = logSheet.getLastRow(); // AUTO FORMAT & DROPDOWN COPY LOGIC FOR REVIEWS if (lastRow > 2) { var sourceRange = logSheet.getRange(lastRow - 1, 1, 1, logSheet.getLastColumn()); var targetRange = logSheet.getRange(lastRow, 1, 1, logSheet.getLastColumn()); sourceRange.copyTo(targetRange, SpreadsheetApp.CopyPasteType.PASTE_DATA_VALIDATION, false); sourceRange.copyTo(targetRange, SpreadsheetApp.CopyPasteType.PASTE_FORMAT, false); } forwardToAdmin(chatID, contents.message.message_id, lastRow, name); return sendReply(chatID, "✅ Hame aapka review mil gaya hai! Quality check ke baad status update kar diya jayega."); } } catch (error) {} return ContentService.createTextOutput("OK"); } function sendReply(chatID, text) { UrlFetchApp.fetch("https://api.telegram.org/bot" + token + "/sendMessage", { method: "post", payload: { "chat_id": chatID, "text": text, "parse_mode": "HTML" }, muteHttpExceptions: true }); } function editMessageText(chatId, messageId, newText) { UrlFetchApp.fetch("https://api.telegram.org/bot" + token + "/editMessageText", { method: "post", payload: { "chat_id": chatId, "message_id": messageId, "text": newText }, muteHttpExceptions: true }); } function answerCallbackQuery(callbackQueryId, text) { UrlFetchApp.fetch("https://api.telegram.org/bot" + token + "/answerCallbackQuery", { method: "post", payload: { "callback_query_id": callbackQueryId, "text": text }, muteHttpExceptions: true }); } function forwardToAdmin(fromChatId, messageId, rowNumber, userName) { if(!adminChatId) return; UrlFetchApp.fetch("https://api.telegram.org/bot" + token + "/forwardMessage", { method: "post", payload: { "chat_id": adminChatId, "from_chat_id": fromChatId, "message_id": messageId }, muteHttpExceptions: true }); var keyboard = { "inline_keyboard": [ [ { "text": "✅ Approve", "callback_data": "approve_" + rowNumber }, { "text": "❌ Reject", "callback_data": "reject_" + rowNumber } ] ] }; UrlFetchApp.fetch("https://api.telegram.org/bot" + token + "/sendMessage", { method: "post", payload: { "chat_id": adminChatId, "text": "Review from: " + userName + "\nDatabase Row: " + rowNumber + "\nAction required:", "reply_markup": JSON.stringify(keyboard) }, muteHttpExceptions: true }); } function doGet(e) { var action = e.parameter.action; var ss = SpreadsheetApp.openById(sheetId); var walletSheet = ss.getSheetByName("User_Wallets"); var data = walletSheet.getDataRange().getValues(); if (action === "auth") { var mobile = e.parameter.mobile; var tgId = e.parameter.tgId; var name = e.parameter.name; var sessionToken = Utilities.getUuid(); for (var i = 1; i < data.length; i++) { if (data[i][2] == tgId) { if (data[i][1] == mobile) { walletSheet.getRange(i + 1, 10).setValue(sessionToken); var pendingAmount = getPendingAmount(ss, tgId); var realBalance = (parseFloat(data[i][8]) || 0) - pendingAmount; var rate = parseFloat(data[i][4]) || 0.10; var currentUnpaidPosts = rate > 0 ? Math.floor(realBalance / rate) : 0; var pendingReviews = getPendingReviewsCount(ss, tgId); var historyData = getWithdrawHistory(ss, tgId); return ContentService.createTextOutput(JSON.stringify({ status: "success", sessionToken: sessionToken, name: data[i][3], rate: rate, approvedPosts: data[i][5], totalEarnings: data[i][6], totalWithdrawn: data[i][7], availableBalance: realBalance, currentUnpaidPosts: currentUnpaidPosts, pendingReviews: pendingReviews, history: historyData })).setMimeType(ContentService.MimeType.JSON); } else { return ContentService.createTextOutput(JSON.stringify({status: "error", message: "Yeh Telegram ID kisi aur Mobile number ke sath link hai!"})).setMimeType(ContentService.MimeType.JSON); } } if (data[i][1] == mobile && data[i][2] != tgId) { return ContentService.createTextOutput(JSON.stringify({status: "error", message: "Yeh Mobile number kisi aur Telegram ID ke sath link hai!"})).setMimeType(ContentService.MimeType.JSON); } } if (!name || name === "User" || name.trim() === "") { return ContentService.createTextOutput(JSON.stringify({status: "error", message: "Account nahi mila! Kripya 'New Member' tab se register karein."})).setMimeType(ContentService.MimeType.JSON); } var newRow = data.length + 1; var date = new Date(); var defaultRate = 0.10; var approvedPostsFormula = '=COUNTIFS(MessagesLog!C:C, C' + newRow + ', MessagesLog!G:G, "Approved")'; var totalEarningsFormula = '=F' + newRow + ' * E' + newRow; var totalWithdrawnFormula = '=SUMIFS(Withdrawal_Requests!D:D, Withdrawal_Requests!B:B, C' + newRow + ', Withdrawal_Requests!F:F, "Paid")'; var availableBalanceFormula = '=G' + newRow + ' - H' + newRow; walletSheet.appendRow([Utilities.formatDate(date, "GMT+05:30", "dd/MM/yyyy"), mobile, tgId, name, defaultRate, approvedPostsFormula, totalEarningsFormula, totalWithdrawnFormula, availableBalanceFormula, sessionToken]); var uLastRow = walletSheet.getLastRow(); // AUTO FORMAT COPY LOGIC FOR NEW USERS if (uLastRow > 2) { var sourceRange = walletSheet.getRange(uLastRow - 1, 1, 1, walletSheet.getLastColumn()); var targetRange = walletSheet.getRange(uLastRow, 1, 1, walletSheet.getLastColumn()); sourceRange.copyTo(targetRange, SpreadsheetApp.CopyPasteType.PASTE_FORMAT, false); } return ContentService.createTextOutput(JSON.stringify({ status: "success", sessionToken: sessionToken, name: name, rate: defaultRate, approvedPosts: 0, totalEarnings: 0, totalWithdrawn: 0, availableBalance: 0, currentUnpaidPosts: 0, pendingReviews: 0, history: [] })).setMimeType(ContentService.MimeType.JSON); } if (action === "withdraw") { var lock = LockService.getScriptLock(); try { lock.waitLock(5000); var tgId = e.parameter.tgId; var amount = parseFloat(e.parameter.amount); var clientToken = e.parameter.sessionToken; if (amount <= 0 || isNaN(amount)) return ContentService.createTextOutput(JSON.stringify({status: "error", message: "Invalid Amount!"})).setMimeType(ContentService.MimeType.JSON); data = walletSheet.getDataRange().getValues(); var hasBalance = false; for (var i = 1; i < data.length; i++) { if (data[i][2] == tgId) { var dbToken = data[i][9]; if (dbToken && clientToken && dbToken !== clientToken) { return ContentService.createTextOutput(JSON.stringify({status: "logout", message: "Security Alert: Aapka account kisi dusre device/tab me login hua hai. Auto Logout ho raha hai."})).setMimeType(ContentService.MimeType.JSON); } var pendingAmount = getPendingAmount(ss, tgId); var realBalance = (parseFloat(data[i][8]) || 0) - pendingAmount; if (realBalance >= amount) hasBalance = true; break; } } if (!hasBalance) return ContentService.createTextOutput(JSON.stringify({status: "error", message: "System Block: Insufficient Balance!"})).setMimeType(ContentService.MimeType.JSON); var withdrawSheet = ss.getSheetByName("Withdrawal_Requests"); withdrawSheet.appendRow([Utilities.formatDate(new Date(), "GMT+05:30", "dd/MM/yyyy"), tgId, e.parameter.name, amount, e.parameter.upi, "Pending"]); var wLastRow = withdrawSheet.getLastRow(); // AUTO FORMAT & DROPDOWN COPY LOGIC FOR WITHDRAWALS if (wLastRow > 2) { var sourceRange = withdrawSheet.getRange(wLastRow - 1, 1, 1, withdrawSheet.getLastColumn()); var targetRange = withdrawSheet.getRange(wLastRow, 1, 1, withdrawSheet.getLastColumn()); sourceRange.copyTo(targetRange, SpreadsheetApp.CopyPasteType.PASTE_DATA_VALIDATION, false); sourceRange.copyTo(targetRange, SpreadsheetApp.CopyPasteType.PASTE_FORMAT, false); } return ContentService.createTextOutput(JSON.stringify({status: "success"})).setMimeType(ContentService.MimeType.JSON); } catch (err) { return ContentService.createTextOutput(JSON.stringify({status: "error", message: "Server busy, please try after 10 seconds!"})).setMimeType(ContentService.MimeType.JSON); } finally { lock.releaseLock(); } } }

Pro - Tracker QMS system/Dine In service OCR and double gmini version support backend gas code text success✅ final

// ========================================== // PT SEVA: DINE-IN SAAS BACKEND (GAS) v13.7 // Model: Smart Subscription Dates + Monthly Sale Shift + Gemini AI Scanner // Status: Column Mapping Fixed (V, W, X, Y) & Bug-Free // ========================================= const SPREADSHEET_ID = "1d2XpS0qW7bW63l6h_ACKzomMJuleUyn2NKFPLCIJfY8"; const S_USERS = "Users"; const S_REST = "Restaurants"; const S_MENU = "Menu"; const S_ORDERS = "Orders"; const S_TXN = "Transactions"; const S_ARCHIVE = "Archive"; const S_ADMIN = "Admin_Settings"; const S_CUSTOMERS = "Customers"; function jsonResponse(data) { return ContentService.createTextOutput(JSON.stringify(data)).setMimeType(ContentService.MimeType.JSON); } function doGet(e) { if (!e || !e.parameter || !e.parameter.action) { return HtmlService.createHtmlOutputFromFile('Index') .setTitle('PT Seva Foodie & Partner App') .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL) .addMetaTag('viewport', 'width=device-width, initial-scale=1'); } const action = e.parameter.action; if (action === "getMenu") return jsonResponse(fetchLiveMenu(e.parameter.restId || "MFF_01")); if (action === "checkStatus") return jsonResponse(checkOrderStatus(e.parameter.orderId)); if (action === "restLogin") return jsonResponse(restLogin(e.parameter.id, e.parameter.pass)); if (action === "getRestStats") return jsonResponse(getRestStats(e.parameter.restId)); if (action === "getOwnerMenu") return jsonResponse(getOwnerMenu(e.parameter.restId)); if (action === "getCustomers") return jsonResponse(getMyCustomers(e.parameter.restId)); if (action === "toggleStore") return jsonResponse(toggleStore(e.parameter.restId)); if (action === "toggleItem") return jsonResponse(toggleItem(e.parameter.itemId, e.parameter.status)); if (action === "updatePrice") return jsonResponse(updateItemPrice(e.parameter.itemId, e.parameter.price)); } function doPost(e) { try { const req = JSON.parse(e.postData.contents); if (req.action === "placeOrder") return jsonResponse(placeOrder(req.orderPayload)); if (req.action === "registerRest") return jsonResponse(registerRestaurant(req.payload)); if (req.action === "addMenuItem") return jsonResponse(addMenuItem(req.payload)); if (req.action === "submitUTR") return jsonResponse(submitMonthlyUTR(req.payload)); if (req.action === "updateStatus") return jsonResponse(updateOrderStatus(req.payload.orderId, req.payload.newStatus, req.payload.etaTime)); if (req.action === "updateOrderItems") return jsonResponse(updateOrderItems(req.payload.orderId, req.payload.itemsStr)); if (req.action === "updateProfile") return jsonResponse(updateProfile(req.payload)); // 🔥 NEW: GEMINI OCR SCANNER ROUTE 🔥 if (req.action === "scanBill") return jsonResponse(processOCRWithGemini(req.payload.base64Image)); } catch (error) { return jsonResponse({ status: "error", message: "Invalid Request Format" }); } } function getSheet(sheetName) { return SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(sheetName); } function fetchLiveMenu(restId) { try { const restSheet = getSheet(S_REST).getDataRange().getValues(); let isStoreOpen = false; let rName = "", rUpi = "", rQr = ""; for (let i = 1; i < restSheet.length; i++) { if (restSheet[i][1] === restId) { if (restSheet[i][11] === "OPEN") isStoreOpen = true; rName = restSheet[i][3]; rUpi = restSheet[i][19]; rQr = restSheet[i][20]; break; } } if (!isStoreOpen) return { status: "error", message: "Sorry, we are currently closed. Please try again later." }; const menuSheet = getSheet(S_MENU).getDataRange().getValues(); let menuData = []; for (let i = 1; i < menuSheet.length; i++) { if (menuSheet[i][0] === restId && menuSheet[i][9] === "Active") { menuData.push({ id: menuSheet[i][1], name: menuSheet[i][2], desc: menuSheet[i][3], price: menuSheet[i][4], type: menuSheet[i][5], category: menuSheet[i][6], img: menuSheet[i][7], unit: menuSheet[i][8] }); } } return { status: "success", data: menuData, restName: rName, restUpi: rUpi, restQr: rQr }; } catch (e) { return { status: "error", message: e.message }; } } function placeOrder(orderPayload) { try { const sheet = getSheet(S_ORDERS); const orderId = "ORD" + Math.floor(10000 + Math.random() * 90000); const timeNow = new Date(); let tableNumOnly = 0; let tMatch = String(orderPayload.tableNumber || "").replace(/\[.*?\]/g, '').match(/T(\d+)/i); if (tMatch) tableNumOnly = parseInt(tMatch[1], 10); const decimal = tableNumOnly > 0 ? (tableNumOnly / 100) : 0; const subTotal = parseFloat(orderPayload.subTotal) || 0; const grandTotal = orderPayload.grandTotal ? parseFloat(orderPayload.grandTotal) : (subTotal + decimal); let finalInstr = orderPayload.instructions || ""; if (orderPayload.isParcel === true) finalInstr = "[📦 PARCEL] " + finalInstr; const orderType = orderPayload.orderType || "ONLINE"; const refId = orderPayload.refId || ""; const tokenPhotoUrl = orderPayload.tokenPhotoUrl || ""; const isPriority = orderPayload.isPriority || "FALSE"; const custType = orderPayload.customerType || "New"; sheet.appendRow([ timeNow, orderId, orderPayload.userPhone, orderPayload.restMobile, orderPayload.restId, orderPayload.restName, orderPayload.items, subTotal.toFixed(2), grandTotal.toFixed(2), decimal.toFixed(2), orderPayload.tableNumber, orderPayload.payMode, "PENDING", timeNow.toLocaleTimeString(), finalInstr, orderPayload.isParcel, custType, "", "", "", orderType, refId, tokenPhotoUrl, isPriority ]); try { const custSheet = getSheet(S_CUSTOMERS); const custData = custSheet.getDataRange().getValues(); let isExistingCustomer = false; for (let i = 1; i < custData.length; i++) { if (custData[i][1] === orderPayload.restId && custData[i][2] == orderPayload.userPhone) { custSheet.getRange(i + 1, 4).setValue(Number(custData[i][3]) + 1); custSheet.getRange(i + 1, 5).setValue(timeNow); isExistingCustomer = true; break; } } if (!isExistingCustomer) custSheet.appendRow([ timeNow, orderPayload.restId, orderPayload.userPhone, 1, timeNow ]); } catch (crmError) { } return { status: "success", orderId: orderId, grandTotal: grandTotal.toFixed(2) }; } catch (e) { return { status: "error", message: e.message }; } } function checkOrderStatus(orderId) { try { const sheet = getSheet(S_ORDERS); const data = sheet.getDataRange().getValues(); const now = new Date(); for (let i = data.length - 1; i >= 1; i--) { if (data[i][1] === orderId) { let stat = data[i][12]; let eta = data[i][19] || ""; let itemsStr = data[i][6] || ""; let payMode = data[i][11] || ""; // Prevent Auto-Cancel for Offline SCANNED & VIP Verify Orders if (stat === "PENDING" && payMode !== "SCANNED" && payMode !== "UPI_VIP_VERIFY") { let orderTime = new Date(data[i][0]); if (Math.floor((now - orderTime) / 60000) >= 10) { sheet.getRange(i + 1, 13).setValue("CANCELLED - AUTO"); stat = "CANCELLED - AUTO"; } } return { status: "success", orderStatus: stat, eta: eta, items: itemsStr }; } } return { status: "error", message: "Order not found" }; } catch (e) { return { status: "error", message: e.message }; } } function restLogin(phone, pass) { try { const sheet = getSheet(S_REST).getDataRange().getValues(); for (let i = 1; i < sheet.length; i++) { if (sheet[i][6] == phone && sheet[i][7] == pass) { if (sheet[i][18] !== "ACTIVE") return { status: "FAIL", msg: "Account pending Admin approval." }; return { status: "SUCCESS", restId: sheet[i][1], name: sheet[i][3] }; } } return { status: "FAIL", msg: "Invalid Mobile or Password." }; } catch (e) { return { status: "FAIL", msg: e.message }; } } function getRestStats(restId) { try { const restSheet = getSheet(S_REST).getDataRange().getValues(); let currentStatus = "CLOSED", profileData = {}; let aggregatedMonthlySale = 0, subFee = 0, subDue = 0; for (let i = 1; i < restSheet.length; i++) { if (restSheet[i][1] === restId) { currentStatus = restSheet[i][11]; // Column Index Alignment // V (21) = Current_Month_Sale // W (22) = Subscription_Fee // Y (24) = Subscription_Due (Assumed next available column after Last_Month_Sale) aggregatedMonthlySale = parseFloat(restSheet[i][21]) || 0; subFee = parseFloat(restSheet[i][22]) || 0; subDue = parseFloat(restSheet[i][24]) || 0; profileData = { mobile: restSheet[i][6], email: restSheet[i][13], fssai: restSheet[i][5], city: restSheet[i][8], address: restSheet[i][9], serviceType: restSheet[i][4], upiId: restSheet[i][19], qrImage: restSheet[i][20], image: restSheet[i][10] }; break; } } let todayStr = new Date().toDateString(); let todayOrders = [], totalSaleLive = 0, totalSaleToday = 0; const now = new Date(); const orderData = getSheet(S_ORDERS).getDataRange().getValues(); for (let i = 1; i < orderData.length; i++) { if (orderData[i][4] === restId) { let orderDateObj = new Date(orderData[i][0]); let orderDate = orderDateObj.toDateString(); let stat = String(orderData[i][12]); let grandTotal = parseFloat(orderData[i][8]) || 0; let payMode = orderData[i][11] || ""; let oType = orderData[i][20] || "ONLINE"; let refId = orderData[i][21] || ""; let tokenPic = orderData[i][22] || ""; if (stat === "PENDING" && Math.floor((now - orderDateObj) / 60000) >= 10) { if (payMode !== "SCANNED" && payMode !== "UPI_VIP_VERIFY") { getSheet(S_ORDERS).getRange(i + 1, 13).setValue("CANCELLED - AUTO"); stat = "CANCELLED - AUTO"; } } if (!stat.includes("REJECTED") && !stat.includes("CANCELLED")) { totalSaleLive += grandTotal; } if (orderDate === todayStr) { if (!stat.includes("REJECTED") && !stat.includes("CANCELLED")) { totalSaleToday += grandTotal; } todayOrders.push({ oid: orderData[i][1], tableNo: orderData[i][10], item: orderData[i][6], amt: grandTotal.toFixed(2), grandTotal: grandTotal.toFixed(2), status: stat, time: orderData[i][13], instr: orderData[i][14] || "", isParcel: orderData[i][15], payMode: payMode, eta: orderData[i][19] || "", orderType: oType, refId: refId, tokenPic: tokenPic }); } } } return { status: "SUCCESS", currentStatus: currentStatus, sale: totalSaleToday.toFixed(2), monthlySale: (aggregatedMonthlySale + totalSaleLive).toFixed(2), subscriptionFee: subFee.toFixed(2), subscriptionDue: subDue.toFixed(2), orders: todayOrders.reverse(), ...profileData }; } catch (e) { return { status: "FAIL", msg: e.message }; } } function toggleStore(restId) { try { const sheet = getSheet(S_REST); const data = sheet.getDataRange().getValues(); for (let i = 1; i < data.length; i++) { if (data[i][1] === restId) { let subDue = parseFloat(data[i][24]) || 0; // Check Due from Column Y (Index 24) let currentStatus = data[i][11]; if (currentStatus === "CLOSED" && subDue > 0) return { status: "FAIL", msg: "⛔ Service Blocked! Please pay your Subscription Due." }; let newStatus = (currentStatus === "OPEN") ? "CLOSED" : "OPEN"; sheet.getRange(i + 1, 12).setValue(newStatus); return { status: "SUCCESS", currentStatus: newStatus }; } } return { status: "FAIL", msg: "Not found" }; } catch (e) { return { status: "FAIL", msg: e.message }; } } function registerRestaurant(data) { try { const sheet = getSheet(S_REST); const existing = sheet.getDataRange().getValues(); for (let i = 1; i < existing.length; i++) { if (existing[i][6] == data.mobile) return { status: "error", message: "Mobile number already registered!" }; } const restId = "RT" + Math.floor(1000 + Math.random() * 9000); sheet.appendRow([ new Date(), restId, data.ownerName, data.shopName, data.serviceType, data.fssai, data.mobile, data.password, data.city, data.address, data.image, "CLOSED", "0", data.email, "No", "", "", "", "PENDING", data.upiId, data.qrImage, 0, 999, 0, 0 // Initializing V, W, X, Y columns ]); return { status: "success", message: "Registration successful. Waiting for Admin Approval.", restId: restId }; } catch (e) { return { status: "error", message: e.message }; } } function updateProfile(data) { try { const sheet = getSheet(S_REST); const rows = sheet.getDataRange().getValues(); for (let i = 1; i < rows.length; i++) { if (rows[i][1] === data.restId) { sheet.getRange(i + 1, 5).setValue(data.serviceType); sheet.getRange(i + 1, 6).setValue(data.fssai); sheet.getRange(i + 1, 7).setValue(data.mobile); if (data.password) sheet.getRange(i + 1, 8).setValue(data.password); sheet.getRange(i + 1, 9).setValue(data.city); sheet.getRange(i + 1, 10).setValue(data.address); if (data.image) sheet.getRange(i + 1, 11).setValue(data.image); sheet.getRange(i + 1, 14).setValue(data.email); sheet.getRange(i + 1, 20).setValue(data.upiId); if (data.qrImage) sheet.getRange(i + 1, 21).setValue(data.qrImage); return { status: "success", message: "Profile Updated Successfully!" }; } } return { status: "error", message: "Account not found" }; } catch (e) { return { status: "error", message: e.message }; } } function getOwnerMenu(restId) { try { const menuSheet = getSheet(S_MENU).getDataRange().getValues(); let menuData = []; for (let i = 1; i < menuSheet.length; i++) { if (menuSheet[i][0] === restId) { menuData.push({ id: menuSheet[i][1], nameEn: menuSheet[i][2], price: menuSheet[i][4], type: menuSheet[i][5], catEn: menuSheet[i][6], img: menuSheet[i][7], unit: menuSheet[i][8], status: menuSheet[i][9] }); } } return { status: "SUCCESS", items: menuData.reverse() }; } catch (e) { return { status: "FAIL", msg: e.message }; } } function addMenuItem(data) { try { const sheet = getSheet(S_MENU); const itemId = "ITM" + Math.floor(1000 + Math.random() * 9000); sheet.appendRow([ data.restId, itemId, data.itemName, data.description || "", data.price, data.foodType, data.category, data.imageUrl || "", data.unit || "", "Active" ]); return { status: "success", message: "Item added successfully", itemId: itemId }; } catch (e) { return { status: "error", message: e.message }; } } function toggleItem(itemId, newStatus) { try { const sheet = getSheet(S_MENU); const data = sheet.getDataRange().getValues(); for (let i = 1; i < data.length; i++) { if (data[i][1] === itemId) { sheet.getRange(i + 1, 10).setValue(newStatus); return { status: "SUCCESS" }; } } return { status: "FAIL", msg: "Item not found" }; } catch (e) { return { status: "FAIL", msg: e.message }; } } function updateItemPrice(itemId, newPrice) { try { const sheet = getSheet(S_MENU); const data = sheet.getDataRange().getValues(); for (let i = 1; i < data.length; i++) { if (data[i][1] === itemId) { sheet.getRange(i + 1, 5).setValue(Number(newPrice)); return { status: "SUCCESS", message: "Price updated" }; } } return { status: "FAIL", msg: "Item not found" }; } catch (e) { return { status: "FAIL", msg: e.message }; } } function updateOrderStatus(orderId, newStatus, etaTime) { try { const sheet = getSheet(S_ORDERS); const data = sheet.getDataRange().getValues(); const timeNow = new Date().toLocaleTimeString(); for (let i = 1; i < data.length; i++) { if (data[i][1] == orderId) { sheet.getRange(i + 1, 13).setValue(newStatus); if (newStatus === "COOKING") { sheet.getRange(i + 1, 18).setValue(timeNow); if (etaTime) sheet.getRange(i + 1, 20).setValue(etaTime); } else if (newStatus === "COMPLETED" || newStatus === "DELIVERED" || newStatus === "READY") { sheet.getRange(i + 1, 19).setValue(timeNow); } return { status: "success", message: "Order marked as " + newStatus }; } } return { status: "error", message: "Order not found" }; } catch (e) { return { status: "error", message: e.message }; } } function updateOrderItems(orderId, itemsStr) { try { const sheet = getSheet(S_ORDERS); const data = sheet.getDataRange().getValues(); for (let i = data.length - 1; i >= 1; i--) { if (data[i][1] === orderId) { sheet.getRange(i + 1, 7).setValue(itemsStr); return { status: "success", message: "Items updated successfully" }; } } return { status: "error", message: "Order not found" }; } catch (e) { return { status: "error", message: e.message }; } } function submitMonthlyUTR(data) { try { const sheet = getSheet(S_TXN); const txnId = "TXN" + Math.floor(100000 + Math.random() * 900000); sheet.appendRow([ new Date(), txnId, data.restId, data.restName, data.billingMonth, data.amountPaid, data.utrNumber, "PENDING", "" ]); return { status: "success", message: "UTR Submitted. Waiting for Admin Verification." }; } catch (e) { return { status: "error", message: e.message }; } } function getMyCustomers(restId) { try { const custSheet = getSheet(S_CUSTOMERS).getDataRange().getValues(); let customerList = []; for (let i = 1; i < custSheet.length; i++) { if (custSheet[i][1] === restId) { customerList.push({ phone: custSheet[i][2], totalOrders: custSheet[i][3], lastOrderDate: new Date(custSheet[i][4]).toDateString() }); } } return { status: "SUCCESS", total: customerList.length, customers: customerList.reverse() }; } catch (e) { return { status: "FAIL", msg: e.message }; } } // 🔥 FEATURE: PERFECT COLUMN MAPPING & MONTHLY ROLLOVER function midnightArchiveCron() { const ordersSheet = getSheet(S_ORDERS); const archiveSheet = getSheet(S_ARCHIVE); const restSheet = getSheet(S_REST); const ordersData = ordersSheet.getDataRange().getValues(); const restData = restSheet.getDataRange().getValues(); let rowsToDelete = []; const today = new Date(); let saleToAdd = {}; // 1. Archive Completed/Cancelled Orders & Calculate Today's Sale for (let i = ordersData.length - 1; i >= 1; i--) { const status = String(ordersData[i][12]); if (status === "COMPLETED" || status === "DELIVERED" || status.includes("REJECTED") || status.includes("CANCELLED")) { if (!status.includes("REJECTED") && !status.includes("CANCELLED")) { let restId = ordersData[i][4]; let grandTotal = parseFloat(ordersData[i][8]) || 0; saleToAdd[restId] = (saleToAdd[restId] || 0) + grandTotal; } let rowData = ordersData[i]; rowData.push(today); archiveSheet.appendRow(rowData); rowsToDelete.push(i + 1); } } rowsToDelete.sort((a,b) => b-a).forEach(rowIdx => ordersSheet.deleteRow(rowIdx)); // 2. Monthly Sale Rollover & Smart Subscription Check const isFirstDayOfMonth = (today.getDate() === 1); for (let i = 1; i < restData.length; i++) { let restId = restData[i][1]; // Column V (21) = Current_Month_Sale // Column W (22) = Subscription_Fee // Column X (23) = Last_Month_Sale // Column Y (24) = Subscription_Due (New safe column for Due) let currentSale = parseFloat(restData[i][21]) || 0; let subFee = parseFloat(restData[i][22]) || 0; let currentDue = parseFloat(restData[i][24]) || 0; // Column R (17) = Subscription_End_Date let endDateVal = restData[i][17]; // 🗓️ Monthly Sale Rollover (1st of Month) if (isFirstDayOfMonth) { restSheet.getRange(i + 1, 24).setValue(currentSale); // Put Current Sale into Last_Month_Sale (Col X) restSheet.getRange(i + 1, 22).setValue(saleToAdd[restId] || 0); // Reset Current Sale to today's sale (Col V) } else { if (saleToAdd[restId] > 0) { restSheet.getRange(i + 1, 22).setValue(currentSale + saleToAdd[restId]); // Add to Current Sale } } // 🔐 Smart Subscription Checking if (endDateVal) { let endDate = new Date(endDateVal); if (!isNaN(endDate.getTime()) && today.getTime() > endDate.getTime()) { if (currentDue === 0 && subFee > 0) { restSheet.getRange(i + 1, 25).setValue(subFee); // Add Due to Column Y (Index 24) restSheet.getRange(i + 1, 12).setValue("CLOSED"); // Lock Store } } } } // 3. Clean up Archive older than 7 Days const archData = archiveSheet.getDataRange().getValues(); for (let i = archData.length - 1; i >= 1; i--) { let archDate = new Date(archData[i][24]); let diffDays = Math.floor((today - archDate) / (1000 * 60 * 60 * 24)); if (diffDays >= 7) { archiveSheet.deleteRow(i + 1); } } } // ========================================== // 🔥 GEMINI AI OCR ENGINE (100% ANTI-LINK CRASH-PROOF) // ========================================== function processOCRWithGemini(base64Image) { try { // 🔥 APNI GEMINI API KEY YAHAN DAALEIN 🔥 var apiKey = "AIzaSyBbJ-Pv1df886JSvHA9zkJTe-G-iBHJGXw"; var payload = { "contents": [{ "parts": [ {"text": "Analyze this restaurant receipt/KOT. 1. Extract the ordered food items and quantities. 2. Extract the main KOT or Token number (e.g., 288). 3. Look at the entire bill to see if it is a 'Swiggy' or 'Zomato' delivery order. \nCRITICAL RULE: If the word 'Swiggy' is printed ANYWHERE on the bill, you MUST combine it with the KOT number in the output (e.g., 'Swiggy 288'). Do NOT return just the number '288'. If 'Zomato' is printed, return 'Zomato 288'. If it's a normal counter bill, return just the number.\nReturn ONLY a pure JSON object. Example: {\"tokenNo\": \"Swiggy 288\", \"items\": [{\"name\": \"Chole Bhature\", \"qty\": 1}]}. If not a receipt, return {\"tokenNo\": \"\", \"items\": []}. Do NOT wrap in ```json markdown."}, { "inline_data": { "mime_type": "image/jpeg", "data": base64Image } } ] }] }; var options = { "method": "post", "contentType": "application/json", "payload": JSON.stringify(payload), "muteHttpExceptions": true }; // 💡 ANTI-LINK CORRUPTION LOGIC: // URL को ऐसे तोड़ा गया है कि कॉपी-पेस्ट में यह कभी लिंक नहीं बनेगा var d1 = "https://generativelanguage"; var d2 = "googleapis"; var d3 = "com"; var path = "/v1beta/models/"; // Code खुद इसे जोड़ेगा: "[https://generativelanguage.googleapis.com/v1beta/models/](https://generativelanguage.googleapis.com/v1beta/models/)" var baseUrl = d1 + "." + d2 + "." + d3 + path; var url1 = baseUrl + "gemini-3.5-flash-lite:generateContent?key=" + apiKey; var url2 = baseUrl + "gemini-3.7-flash:generateContent?key=" + apiKey; // --- EXACT ORIGINAL LOGIC (WITH DUAL FALLBACK) --- var response = UrlFetchApp.fetch(url1, options); var json = JSON.parse(response.getContentText()); // अगर 3.5 Flash-Lite बिजी (High Demand) है, तो Fallback चलेगा if (json.error) { response = UrlFetchApp.fetch(url2, options); json = JSON.parse(response.getContentText()); // अगर 3.7 Flash भी फेल हो जाए, तो एरर दिखाओ if (json.error) { return { status: "error", message: json.error.message }; } } var aiResponse = json.candidates[0].content.parts[0].text; // Clean markdown tags aiResponse = aiResponse.replace(/```json/gi, '').replace(/```/g, '').trim(); return { status: "success", text: aiResponse }; } catch(err) { return { status: "error", message: "Gemini API Error: " + err.toString() }; } }

Gemini Scanner OCR tracker for qms, backend script gas code.✅

// ========================================== // PT SEVA: DINE-IN SAAS BACKEND (GAS) v13.7 // Model: Smart Subscription Dates + Monthly Sale Shift + Gemini AI Scanner // Status: Column Mapping Fixed (V, W, X, Y) & Bug-Free // ========================================= const SPREADSHEET_ID = "18yq9r94BI-HR5NbhyDr-fgV3FcptahsxplRFmYq9Yfo"; const S_USERS = "Users"; const S_REST = "Restaurants"; const S_MENU = "Menu"; const S_ORDERS = "Orders"; const S_TXN = "Transactions"; const S_ARCHIVE = "Archive"; const S_ADMIN = "Admin_Settings"; const S_CUSTOMERS = "Customers"; function jsonResponse(data) { return ContentService.createTextOutput(JSON.stringify(data)).setMimeType(ContentService.MimeType.JSON); } function doGet(e) { if (!e || !e.parameter || !e.parameter.action) { return HtmlService.createHtmlOutputFromFile('Index') .setTitle('PT Seva Foodie & Partner App') .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL) .addMetaTag('viewport', 'width=device-width, initial-scale=1'); } const action = e.parameter.action; if (action === "getMenu") return jsonResponse(fetchLiveMenu(e.parameter.restId || "MFF_01")); if (action === "checkStatus") return jsonResponse(checkOrderStatus(e.parameter.orderId)); if (action === "restLogin") return jsonResponse(restLogin(e.parameter.id, e.parameter.pass)); if (action === "getRestStats") return jsonResponse(getRestStats(e.parameter.restId)); if (action === "getOwnerMenu") return jsonResponse(getOwnerMenu(e.parameter.restId)); if (action === "getCustomers") return jsonResponse(getMyCustomers(e.parameter.restId)); if (action === "toggleStore") return jsonResponse(toggleStore(e.parameter.restId)); if (action === "toggleItem") return jsonResponse(toggleItem(e.parameter.itemId, e.parameter.status)); if (action === "updatePrice") return jsonResponse(updateItemPrice(e.parameter.itemId, e.parameter.price)); } function doPost(e) { try { const req = JSON.parse(e.postData.contents); if (req.action === "placeOrder") return jsonResponse(placeOrder(req.orderPayload)); if (req.action === "registerRest") return jsonResponse(registerRestaurant(req.payload)); if (req.action === "addMenuItem") return jsonResponse(addMenuItem(req.payload)); if (req.action === "submitUTR") return jsonResponse(submitMonthlyUTR(req.payload)); if (req.action === "updateStatus") return jsonResponse(updateOrderStatus(req.payload.orderId, req.payload.newStatus, req.payload.etaTime)); if (req.action === "updateOrderItems") return jsonResponse(updateOrderItems(req.payload.orderId, req.payload.itemsStr)); if (req.action === "updateProfile") return jsonResponse(updateProfile(req.payload)); // 🔥 NEW: GEMINI OCR SCANNER ROUTE 🔥 if (req.action === "scanBill") return jsonResponse(processOCRWithGemini(req.payload.base64Image)); } catch (error) { return jsonResponse({ status: "error", message: "Invalid Request Format" }); } } function getSheet(sheetName) { return SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(sheetName); } function fetchLiveMenu(restId) { try { const restSheet = getSheet(S_REST).getDataRange().getValues(); let isStoreOpen = false; let rName = "", rUpi = "", rQr = ""; for (let i = 1; i < restSheet.length; i++) { if (restSheet[i][1] === restId) { if (restSheet[i][11] === "OPEN") isStoreOpen = true; rName = restSheet[i][3]; rUpi = restSheet[i][19]; rQr = restSheet[i][20]; break; } } if (!isStoreOpen) return { status: "error", message: "Sorry, we are currently closed. Please try again later." }; const menuSheet = getSheet(S_MENU).getDataRange().getValues(); let menuData = []; for (let i = 1; i < menuSheet.length; i++) { if (menuSheet[i][0] === restId && menuSheet[i][9] === "Active") { menuData.push({ id: menuSheet[i][1], name: menuSheet[i][2], desc: menuSheet[i][3], price: menuSheet[i][4], type: menuSheet[i][5], category: menuSheet[i][6], img: menuSheet[i][7], unit: menuSheet[i][8] }); } } return { status: "success", data: menuData, restName: rName, restUpi: rUpi, restQr: rQr }; } catch (e) { return { status: "error", message: e.message }; } } function placeOrder(orderPayload) { try { const sheet = getSheet(S_ORDERS); const orderId = "ORD" + Math.floor(10000 + Math.random() * 90000); const timeNow = new Date(); let tableNumOnly = 0; let tMatch = String(orderPayload.tableNumber || "").replace(/\[.*?\]/g, '').match(/T(\d+)/i); if (tMatch) tableNumOnly = parseInt(tMatch[1], 10); const decimal = tableNumOnly > 0 ? (tableNumOnly / 100) : 0; const subTotal = parseFloat(orderPayload.subTotal) || 0; const grandTotal = orderPayload.grandTotal ? parseFloat(orderPayload.grandTotal) : (subTotal + decimal); let finalInstr = orderPayload.instructions || ""; if (orderPayload.isParcel === true) finalInstr = "[📦 PARCEL] " + finalInstr; const orderType = orderPayload.orderType || "ONLINE"; const refId = orderPayload.refId || ""; const tokenPhotoUrl = orderPayload.tokenPhotoUrl || ""; const isPriority = orderPayload.isPriority || "FALSE"; const custType = orderPayload.customerType || "New"; sheet.appendRow([ timeNow, orderId, orderPayload.userPhone, orderPayload.restMobile, orderPayload.restId, orderPayload.restName, orderPayload.items, subTotal.toFixed(2), grandTotal.toFixed(2), decimal.toFixed(2), orderPayload.tableNumber, orderPayload.payMode, "PENDING", timeNow.toLocaleTimeString(), finalInstr, orderPayload.isParcel, custType, "", "", "", orderType, refId, tokenPhotoUrl, isPriority ]); try { const custSheet = getSheet(S_CUSTOMERS); const custData = custSheet.getDataRange().getValues(); let isExistingCustomer = false; for (let i = 1; i < custData.length; i++) { if (custData[i][1] === orderPayload.restId && custData[i][2] == orderPayload.userPhone) { custSheet.getRange(i + 1, 4).setValue(Number(custData[i][3]) + 1); custSheet.getRange(i + 1, 5).setValue(timeNow); isExistingCustomer = true; break; } } if (!isExistingCustomer) custSheet.appendRow([ timeNow, orderPayload.restId, orderPayload.userPhone, 1, timeNow ]); } catch (crmError) { } return { status: "success", orderId: orderId, grandTotal: grandTotal.toFixed(2) }; } catch (e) { return { status: "error", message: e.message }; } } function checkOrderStatus(orderId) { try { const sheet = getSheet(S_ORDERS); const data = sheet.getDataRange().getValues(); const now = new Date(); for (let i = data.length - 1; i >= 1; i--) { if (data[i][1] === orderId) { let stat = data[i][12]; let eta = data[i][19] || ""; let itemsStr = data[i][6] || ""; let payMode = data[i][11] || ""; // Prevent Auto-Cancel for Offline SCANNED & VIP Verify Orders if (stat === "PENDING" && payMode !== "SCANNED" && payMode !== "UPI_VIP_VERIFY") { let orderTime = new Date(data[i][0]); if (Math.floor((now - orderTime) / 60000) >= 10) { sheet.getRange(i + 1, 13).setValue("CANCELLED - AUTO"); stat = "CANCELLED - AUTO"; } } return { status: "success", orderStatus: stat, eta: eta, items: itemsStr }; } } return { status: "error", message: "Order not found" }; } catch (e) { return { status: "error", message: e.message }; } } function restLogin(phone, pass) { try { const sheet = getSheet(S_REST).getDataRange().getValues(); for (let i = 1; i < sheet.length; i++) { if (sheet[i][6] == phone && sheet[i][7] == pass) { if (sheet[i][18] !== "ACTIVE") return { status: "FAIL", msg: "Account pending Admin approval." }; return { status: "SUCCESS", restId: sheet[i][1], name: sheet[i][3] }; } } return { status: "FAIL", msg: "Invalid Mobile or Password." }; } catch (e) { return { status: "FAIL", msg: e.message }; } } function getRestStats(restId) { try { const restSheet = getSheet(S_REST).getDataRange().getValues(); let currentStatus = "CLOSED", profileData = {}; let aggregatedMonthlySale = 0, subFee = 0, subDue = 0; for (let i = 1; i < restSheet.length; i++) { if (restSheet[i][1] === restId) { currentStatus = restSheet[i][11]; // Column Index Alignment // V (21) = Current_Month_Sale // W (22) = Subscription_Fee // Y (24) = Subscription_Due (Assumed next available column after Last_Month_Sale) aggregatedMonthlySale = parseFloat(restSheet[i][21]) || 0; subFee = parseFloat(restSheet[i][22]) || 0; subDue = parseFloat(restSheet[i][24]) || 0; profileData = { mobile: restSheet[i][6], email: restSheet[i][13], fssai: restSheet[i][5], city: restSheet[i][8], address: restSheet[i][9], serviceType: restSheet[i][4], upiId: restSheet[i][19], qrImage: restSheet[i][20], image: restSheet[i][10] }; break; } } let todayStr = new Date().toDateString(); let todayOrders = [], totalSaleLive = 0, totalSaleToday = 0; const now = new Date(); const orderData = getSheet(S_ORDERS).getDataRange().getValues(); for (let i = 1; i < orderData.length; i++) { if (orderData[i][4] === restId) { let orderDateObj = new Date(orderData[i][0]); let orderDate = orderDateObj.toDateString(); let stat = String(orderData[i][12]); let grandTotal = parseFloat(orderData[i][8]) || 0; let payMode = orderData[i][11] || ""; let oType = orderData[i][20] || "ONLINE"; let refId = orderData[i][21] || ""; let tokenPic = orderData[i][22] || ""; if (stat === "PENDING" && Math.floor((now - orderDateObj) / 60000) >= 10) { if (payMode !== "SCANNED" && payMode !== "UPI_VIP_VERIFY") { getSheet(S_ORDERS).getRange(i + 1, 13).setValue("CANCELLED - AUTO"); stat = "CANCELLED - AUTO"; } } if (!stat.includes("REJECTED") && !stat.includes("CANCELLED")) { totalSaleLive += grandTotal; } if (orderDate === todayStr) { if (!stat.includes("REJECTED") && !stat.includes("CANCELLED")) { totalSaleToday += grandTotal; } todayOrders.push({ oid: orderData[i][1], tableNo: orderData[i][10], item: orderData[i][6], amt: grandTotal.toFixed(2), grandTotal: grandTotal.toFixed(2), status: stat, time: orderData[i][13], instr: orderData[i][14] || "", isParcel: orderData[i][15], payMode: payMode, eta: orderData[i][19] || "", orderType: oType, refId: refId, tokenPic: tokenPic }); } } } return { status: "SUCCESS", currentStatus: currentStatus, sale: totalSaleToday.toFixed(2), monthlySale: (aggregatedMonthlySale + totalSaleLive).toFixed(2), subscriptionFee: subFee.toFixed(2), subscriptionDue: subDue.toFixed(2), orders: todayOrders.reverse(), ...profileData }; } catch (e) { return { status: "FAIL", msg: e.message }; } } function toggleStore(restId) { try { const sheet = getSheet(S_REST); const data = sheet.getDataRange().getValues(); for (let i = 1; i < data.length; i++) { if (data[i][1] === restId) { let subDue = parseFloat(data[i][24]) || 0; // Check Due from Column Y (Index 24) let currentStatus = data[i][11]; if (currentStatus === "CLOSED" && subDue > 0) return { status: "FAIL", msg: "⛔ Service Blocked! Please pay your Subscription Due." }; let newStatus = (currentStatus === "OPEN") ? "CLOSED" : "OPEN"; sheet.getRange(i + 1, 12).setValue(newStatus); return { status: "SUCCESS", currentStatus: newStatus }; } } return { status: "FAIL", msg: "Not found" }; } catch (e) { return { status: "FAIL", msg: e.message }; } } function registerRestaurant(data) { try { const sheet = getSheet(S_REST); const existing = sheet.getDataRange().getValues(); for (let i = 1; i < existing.length; i++) { if (existing[i][6] == data.mobile) return { status: "error", message: "Mobile number already registered!" }; } const restId = "RT" + Math.floor(1000 + Math.random() * 9000); sheet.appendRow([ new Date(), restId, data.ownerName, data.shopName, data.serviceType, data.fssai, data.mobile, data.password, data.city, data.address, data.image, "CLOSED", "0", data.email, "No", "", "", "", "PENDING", data.upiId, data.qrImage, 0, 999, 0, 0 // Initializing V, W, X, Y columns ]); return { status: "success", message: "Registration successful. Waiting for Admin Approval.", restId: restId }; } catch (e) { return { status: "error", message: e.message }; } } function updateProfile(data) { try { const sheet = getSheet(S_REST); const rows = sheet.getDataRange().getValues(); for (let i = 1; i < rows.length; i++) { if (rows[i][1] === data.restId) { sheet.getRange(i + 1, 5).setValue(data.serviceType); sheet.getRange(i + 1, 6).setValue(data.fssai); sheet.getRange(i + 1, 7).setValue(data.mobile); if (data.password) sheet.getRange(i + 1, 8).setValue(data.password); sheet.getRange(i + 1, 9).setValue(data.city); sheet.getRange(i + 1, 10).setValue(data.address); if (data.image) sheet.getRange(i + 1, 11).setValue(data.image); sheet.getRange(i + 1, 14).setValue(data.email); sheet.getRange(i + 1, 20).setValue(data.upiId); if (data.qrImage) sheet.getRange(i + 1, 21).setValue(data.qrImage); return { status: "success", message: "Profile Updated Successfully!" }; } } return { status: "error", message: "Account not found" }; } catch (e) { return { status: "error", message: e.message }; } } function getOwnerMenu(restId) { try { const menuSheet = getSheet(S_MENU).getDataRange().getValues(); let menuData = []; for (let i = 1; i < menuSheet.length; i++) { if (menuSheet[i][0] === restId) { menuData.push({ id: menuSheet[i][1], nameEn: menuSheet[i][2], price: menuSheet[i][4], type: menuSheet[i][5], catEn: menuSheet[i][6], img: menuSheet[i][7], unit: menuSheet[i][8], status: menuSheet[i][9] }); } } return { status: "SUCCESS", items: menuData.reverse() }; } catch (e) { return { status: "FAIL", msg: e.message }; } } function addMenuItem(data) { try { const sheet = getSheet(S_MENU); const itemId = "ITM" + Math.floor(1000 + Math.random() * 9000); sheet.appendRow([ data.restId, itemId, data.itemName, data.description || "", data.price, data.foodType, data.category, data.imageUrl || "", data.unit || "", "Active" ]); return { status: "success", message: "Item added successfully", itemId: itemId }; } catch (e) { return { status: "error", message: e.message }; } } function toggleItem(itemId, newStatus) { try { const sheet = getSheet(S_MENU); const data = sheet.getDataRange().getValues(); for (let i = 1; i < data.length; i++) { if (data[i][1] === itemId) { sheet.getRange(i + 1, 10).setValue(newStatus); return { status: "SUCCESS" }; } } return { status: "FAIL", msg: "Item not found" }; } catch (e) { return { status: "FAIL", msg: e.message }; } } function updateItemPrice(itemId, newPrice) { try { const sheet = getSheet(S_MENU); const data = sheet.getDataRange().getValues(); for (let i = 1; i < data.length; i++) { if (data[i][1] === itemId) { sheet.getRange(i + 1, 5).setValue(Number(newPrice)); return { status: "SUCCESS", message: "Price updated" }; } } return { status: "FAIL", msg: "Item not found" }; } catch (e) { return { status: "FAIL", msg: e.message }; } } function updateOrderStatus(orderId, newStatus, etaTime) { try { const sheet = getSheet(S_ORDERS); const data = sheet.getDataRange().getValues(); const timeNow = new Date().toLocaleTimeString(); for (let i = 1; i < data.length; i++) { if (data[i][1] == orderId) { sheet.getRange(i + 1, 13).setValue(newStatus); if (newStatus === "COOKING") { sheet.getRange(i + 1, 18).setValue(timeNow); if (etaTime) sheet.getRange(i + 1, 20).setValue(etaTime); } else if (newStatus === "COMPLETED" || newStatus === "DELIVERED" || newStatus === "READY") { sheet.getRange(i + 1, 19).setValue(timeNow); } return { status: "success", message: "Order marked as " + newStatus }; } } return { status: "error", message: "Order not found" }; } catch (e) { return { status: "error", message: e.message }; } } function updateOrderItems(orderId, itemsStr) { try { const sheet = getSheet(S_ORDERS); const data = sheet.getDataRange().getValues(); for (let i = data.length - 1; i >= 1; i--) { if (data[i][1] === orderId) { sheet.getRange(i + 1, 7).setValue(itemsStr); return { status: "success", message: "Items updated successfully" }; } } return { status: "error", message: "Order not found" }; } catch (e) { return { status: "error", message: e.message }; } } function submitMonthlyUTR(data) { try { const sheet = getSheet(S_TXN); const txnId = "TXN" + Math.floor(100000 + Math.random() * 900000); sheet.appendRow([ new Date(), txnId, data.restId, data.restName, data.billingMonth, data.amountPaid, data.utrNumber, "PENDING", "" ]); return { status: "success", message: "UTR Submitted. Waiting for Admin Verification." }; } catch (e) { return { status: "error", message: e.message }; } } function getMyCustomers(restId) { try { const custSheet = getSheet(S_CUSTOMERS).getDataRange().getValues(); let customerList = []; for (let i = 1; i < custSheet.length; i++) { if (custSheet[i][1] === restId) { customerList.push({ phone: custSheet[i][2], totalOrders: custSheet[i][3], lastOrderDate: new Date(custSheet[i][4]).toDateString() }); } } return { status: "SUCCESS", total: customerList.length, customers: customerList.reverse() }; } catch (e) { return { status: "FAIL", msg: e.message }; } } // 🔥 FEATURE: PERFECT COLUMN MAPPING & MONTHLY ROLLOVER function midnightArchiveCron() { const ordersSheet = getSheet(S_ORDERS); const archiveSheet = getSheet(S_ARCHIVE); const restSheet = getSheet(S_REST); const ordersData = ordersSheet.getDataRange().getValues(); const restData = restSheet.getDataRange().getValues(); let rowsToDelete = []; const today = new Date(); let saleToAdd = {}; // 1. Archive Completed/Cancelled Orders & Calculate Today's Sale for (let i = ordersData.length - 1; i >= 1; i--) { const status = String(ordersData[i][12]); if (status === "COMPLETED" || status === "DELIVERED" || status.includes("REJECTED") || status.includes("CANCELLED")) { if (!status.includes("REJECTED") && !status.includes("CANCELLED")) { let restId = ordersData[i][4]; let grandTotal = parseFloat(ordersData[i][8]) || 0; saleToAdd[restId] = (saleToAdd[restId] || 0) + grandTotal; } let rowData = ordersData[i]; rowData.push(today); archiveSheet.appendRow(rowData); rowsToDelete.push(i + 1); } } rowsToDelete.sort((a,b) => b-a).forEach(rowIdx => ordersSheet.deleteRow(rowIdx)); // 2. Monthly Sale Rollover & Smart Subscription Check const isFirstDayOfMonth = (today.getDate() === 1); for (let i = 1; i < restData.length; i++) { let restId = restData[i][1]; // Column V (21) = Current_Month_Sale // Column W (22) = Subscription_Fee // Column X (23) = Last_Month_Sale // Column Y (24) = Subscription_Due (New safe column for Due) let currentSale = parseFloat(restData[i][21]) || 0; let subFee = parseFloat(restData[i][22]) || 0; let currentDue = parseFloat(restData[i][24]) || 0; // Column R (17) = Subscription_End_Date let endDateVal = restData[i][17]; // 🗓️ Monthly Sale Rollover (1st of Month) if (isFirstDayOfMonth) { restSheet.getRange(i + 1, 24).setValue(currentSale); // Put Current Sale into Last_Month_Sale (Col X) restSheet.getRange(i + 1, 22).setValue(saleToAdd[restId] || 0); // Reset Current Sale to today's sale (Col V) } else { if (saleToAdd[restId] > 0) { restSheet.getRange(i + 1, 22).setValue(currentSale + saleToAdd[restId]); // Add to Current Sale } } // 🔐 Smart Subscription Checking if (endDateVal) { let endDate = new Date(endDateVal); if (!isNaN(endDate.getTime()) && today.getTime() > endDate.getTime()) { if (currentDue === 0 && subFee > 0) { restSheet.getRange(i + 1, 25).setValue(subFee); // Add Due to Column Y (Index 24) restSheet.getRange(i + 1, 12).setValue("CLOSED"); // Lock Store } } } } // 3. Clean up Archive older than 7 Days const archData = archiveSheet.getDataRange().getValues(); for (let i = archData.length - 1; i >= 1; i--) { let archDate = new Date(archData[i][24]); let diffDays = Math.floor((today - archDate) / (1000 * 60 * 60 * 24)); if (diffDays >= 7) { archiveSheet.deleteRow(i + 1); } } } // ========================================= // 🔥 GEMINI AI OCR ENGINE (SMART JSON MODE) // ========================================== function processOCRWithGemini(base64Image) { try { // 🔥 APNI GEMINI API KEY YAHAN DAALEIN 🔥 var apiKey = "AIzaSyBbJ-Pv1df886JSvHA9zkJTe-G-iBHJGXw"; var url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.5-flash-lite:generateContent?key=" + apiKey; // 🔥 MAGIC PROMPT (UPDATED): AI ko Token No. aur Cart Items dono ek JSON Object mein dene ka order diya var payload = { "contents": [{ "parts": [ {"text": "Analyze this image. If it is a restaurant receipt or KOT, extract the Token/Bill/Order number and ONLY the ordered food/drink items with their quantities. Return the result STRICTLY as a JSON object. Example Format: {\"tokenNo\": \"25\", \"items\": [{\"name\": \"Dosa Spl\", \"qty\": 1}, {\"name\": \"Water Bottle 1lt\", \"qty\": 2}]}. If no token number is found, leave it empty: \"\". If the image is NOT a receipt or has no food items, return empty items: {\"tokenNo\": \"\", \"items\": []}. Do NOT include prices, totals, GST, or any other text. Do NOT wrap it in ```json markdown, just output the pure JSON object."}, { "inline_data": { "mime_type": "image/jpeg", "data": base64Image } } ] }] }; var options = { "method": "post", "contentType": "application/json", "payload": JSON.stringify(payload), "muteHttpExceptions": true }; var response = UrlFetchApp.fetch(url, options); var json = JSON.parse(response.getContentText()); if (json.error) { return { status: "error", message: json.error.message }; } var aiResponse = json.candidates[0].content.parts[0].text; // Clean markdown tags if AI sends them by mistake aiResponse = aiResponse.replace(/```json/gi, '').replace(/```/g, '').trim(); return { status: "success", text: aiResponse }; } catch(e) { return { status: "error", message: "Gemini API Error: " + e.toString() }; } }
Menu, Order & Track | PT Seva v13.1 PRO
⚠️ You are offline. Please check your internet connection.
Loading...
Select Table
Item Total | ₹ 0.00

Payment Verified!

Thank you, you are able to get services on your table, Plz track your order.