Track App Scanner Backend Gas Code Success ✅
// ==========================================
// 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 = "18hjPH8aBJmT8xzCU1k56QIVvH_u4R79xk31wBVA-fY4";
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() };
}
}
GEMINI OCR SCANNER - SMART JSON TEST GAS SCRIPT✅success items fetch from bills/Tokens
// ==========================================
// GEMINI OCR SCANNER - SMART JSON TEST SCRIPT
// ==========================================
function doPost(e) {
try {
var data = JSON.parse(e.postData.contents);
// Check if the payload is coming from the HTML format we set up
var base64Image = data.payload ? data.payload.base64Image : data.image.split(',')[1];
// 🔥 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: AI ko seedha Cart banane ka order diya
var payload = {
"contents": [{
"parts": [
{"text": "Extract ONLY the ordered food/drink items and their quantities from this receipt. Return the result STRICTLY as a JSON array of objects. Example Format: [{\"name\": \"Dosa Spl\", \"qty\": 1}, {\"name\": \"Water Bottle 1lt\", \"qty\": 2}]. Do NOT include prices, totals, GST, or any other text. Do NOT wrap it in ```json markdown, just output the pure JSON array."},
{
"inline_data": {
"mime_type": "image/jpeg",
"data": base64Image
}
}
]
}]
};
var options = {
"method": "post",
"contentType": "application/json",
"payload": JSON.stringify(payload),
"muteHttpExceptions": true // Catch errors without failing the script
};
var response = UrlFetchApp.fetch(url, options);
var json = JSON.parse(response.getContentText());
if(json.error) {
return ContentService.createTextOutput(JSON.stringify({ status: "error", message: json.error.message })).setMimeType(ContentService.MimeType.JSON);
}
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 ContentService.createTextOutput(JSON.stringify({ status: "success", text: aiResponse })).setMimeType(ContentService.MimeType.JSON);
} catch(err) {
return ContentService.createTextOutput(JSON.stringify({ status: "error", message: err.toString() })).setMimeType(ContentService.MimeType.JSON);
}
}
// CORS ke liye zaroori
function doOptions(e) {
return ContentService.createTextOutput("OK").setMimeType(ContentService.MimeType.TEXT);
}
// Permission Bypass Test Function
function forceAuth() {
UrlFetchApp.fetch("[https://www.google.com](https://www.google.com)");
}
Super Clean Test HTML
AI OCR Cart Test (Smart JSON)
1. Upload Bill for Scanning
Waiting for bill image...
2. Auto-Drafted Cart
TokenTap QMS
TokenTap - The Queue Managing System by PT SEVA
Aaj ke waqt mein sabse achha khana banana hi kaafi nahi hai; sabse fast aur shanti-purvak experience dena bhi utna hi zaroori hai. Bheed khatam karein aur profit badhayein!
Hamara 3-App Ecosystem
Ek aisi technology jo aapke Counter, Kitchen aur Customer, teeno ko aapas mein live connect karti hai.
1. Customer App (Foodie)
- Live Order Tracking: Customer table par baith kar apne order ka live ETA dekhta hai.
- Offline Token Track: Counter par order dene ke baad bas apna Token/Bill number daal kar live track karein, bina kisi scan ya photo ke!
- Sound Alerts: Khana ready hote hi customer ke phone par 15 second ka zor-daar alarm bajta है.
2. Manager Dashboard
- Smart Routing: Online aur VIP orders ko verify karne ke baad hi kitchen bhejein.
- Live Stats & CRM: Daily sales track karein aur apne customers ka data (WhatsApp CRM) 1-click mein download karein.
- Thermal Print KOT: Kisi bhi order ki Day-End summary ya KOT receipt instantly print karein.
3. Kitchen Display (KDS)
- Clean & Loud UI: Kitchen ke shore mein naya order aate hi loud alarm bajta hai.
- Item-Level Tracking: Chef har item ko "Ready" kar sakta hai, aur "Send" dabate hi customer ko live update chala jata hai.
- Instant Out-of-Stock: Kitchen se item OOS mark karte hi, customer app par turant block ho jata hai.
Live Workflow Showcase
Step 1 New Order From Table / Offline
Cart Screen
Live Tracking Start
Step 2 Manager Acceptance ➔ Kitchen KDS
Manager Dashboard View
Step 3 Live Updates to Customer (With Sound Alerts)
Manager / Staff View
Customer App (Live Status)
Business Owners Ko Fayda
- Bheed-Free Counter: Customers order dekar aaram se baith jate hain. Counter clear rehta hai.
- Zero Hardware Cost: Hame kisi mehangi screen ya physical buzzer ki zarurat nahi.
- Staff Ki Shanti: Kitchen aur counter staff bina argue kiye focus se kaam karte hain. Order speed 2x ho jati hai.
Customers Ko Fayda
- Premium VIP Experience: Customer ko line mein dhakke khane ki zarurat nahi. Unka smartphone hi tracker ban jata hai.
- Perfect For Cinemas & Theatres: Interval ki line mein mat lagon. Token daalo, seat par relax karo, aur silent alert aane par order pick karo!
- No Frustration: Jab customer ko live Wait Time (ETA) dikhta hai, toh intezaar ka gussa khatam ho jata hai.
TokenTap QMS Ko Aaj Hi Aazmayein!
Apne restaurant ko premium banayein. Zero risk setup aur 10 din ke free trial ke sath apne business ki speed 2x karein.
Start 10-Day Free TrialNo Credit Card Required • Setup in 10 minutes
×
Order Flow
New Order from table photo.
Manager Dashboard se accept karne par kitchen me turant order kaise jata hai aur kaise dikhta hai, photo.
LINKS CONTAINER
Channel Deals & PT Seva Master Directory
🔍
Important Feedback Forms
"Sir/Brother, Channel का full access (Primary Owner) अब हमारे पास है, और हमने आपकी पेमेंट ✅ भी कर दी है। Deal close करने के लिए बस 2 मिनट निकालकर ये feedback दे दीजिये, ताकि भविष्य में लोग हम पर भरोसा कर सकें:"
1. Deal start करने से पहले और चैनल का Owner एक्सेस पहले देने में, क्या आपको Scam का डर लग रहा था, और अब deal complete होने के बाद आप कैसा feel कर रहे हैं?
A) Happy
B) Unhappy
C) Sad
D) Very Happy 😊
2. जो चैनल आपने हमे बेचा है वो कैसा था, जैसे
A. Monetized
B. Non-Monetized
3. आपके चैनल में लगभग कितने सब्सक्राइबर्स थे ?
4. Account handover/Primary Access के बाद आपको Payment कितनी देर में receive हुई? क्या process fast था?
5. पूरी dealing के दौरान हमारा behavior और समझाने का तरीका (guidance) आपको कैसा लगा?
6. "अगर आपके किसी friend को channel बेचना हो, तो क्या आप हमारा name (Channel Deal) recommend करेंगे?"
7. हमारी service को 5 में से कितने Star देंगे? (⭐⭐⭐⭐⭐)"
8. क्या आप बता सकते है कि आपको हमारे बारे में जानकारी कहाँ से मिली थी ? जैसे,
A) दोस्त से
B) Youtube से
C) Telegram से
D) Instagram से
E) Facebook से
9. "Brother, एक छोटी सी request है। अगर आप एक 30-second का सेल्फी वीडियो बनाकर भेज सकें तो यह मेरे लिए बहुत help होगी। कि — 'मैंने अपना चैनल [Channel Deals] को बेचा, और मुझे payment successfully मिल गयी है' — तो यह मेरे लिए बहुत help होगी।"
Last Message,
"डील सफलतापूर्वक पूरी हो गई है! पेमेंट आपके पास पहुँच चुकी है और अब मैंने चैनल की ओनरशिप पूरी तरह ट्रांसफर कर ली है। मुझ पर भरोसा करने और 7 दिनों तक धैर्य रखने के लिए बहुत-बहुत धन्यवाद।
आपके साथ काम करके खुशी हुई। भविष्य में अगर कोई और चैनल बेचना हो या किसी दोस्त को बेचना हो, तो याद रखिएगा। धन्यवाद!
"Thank you for your important feedback🤝.... Channel Deals (PT Seva)"
"Sir/Brother, Channel का (Owner) access अब आपके पास है।
7 दिन होते ही आपको full access (Primary Owner) दे दिया जाएगा, और अपनी डील पूरी हो जायेगी and Deal close हो जायेगी।
अभी आप बस 2 मिनट निकालकर ये feedback दे दीजिये, ताकि भविष्य में लोग हम पर भरोसा कर सकें:"
"Sir/Brother, Channel का full access (Primary Owner) अब आपके पास है। Deal close करने के लिए बस 2 मिनट निकालकर ये feedback दे दीजिये, ताकि भविष्य में लोग हम पर भरोसा कर सकें:"
1. हमें Payment पहले (Advance) भेजने में क्या आपको डर लग रहा था? और अब चैनल मिलने के बाद आप कैसा महसूस कर रहे हैं?"
A) Happy
B) Unhappy
2. जो चैनल आपने, हमसे लिया/ख़रीदा है, वो कैसा था, जैसे
A. Monetized
B. Non-Monetized
3. जो चैनल आपने हमसे लिया उसमें लगभग कितना सब्सक्राइबर हैं?
4. "Google की पॉलिसी की वजह से हमें 7 दिन रुकना पड़ा। क्या इन 7 दिनों में मैंने आपको properly guide किया और आपके calls/messages का जवाब दिया?"
5. "जैसा हमने चैनल दिखाया था, क्या आपको exactly वैसा ही चैनल मिला?
6. "अगर आपके किसी दोस्त को Safe तरीके से चैनल खरीदना हो, तो क्या आप हमारा नाम (Channel Deals by PT Seva) suggest करेंगे?"
7. "Trust और Service के लिए आप हमें 5 में से कितने Star देंगे? ⭐⭐⭐⭐⭐"
8. क्या आप बता सकते है कि आपको हमारे बारे में जानकारी कहाँ से मिली थी ? जैसे,
A) दोस्त से
B) Youtube से
C) Telegram से
D) Instagram से
E) Facebook से
9. "Thank you for your important feedback brother 🤝.... Channel Deals"
10. "Sir/Brother, एक छोटी सी request है। अगर आप एक 30-second का सेल्फी वीडियो बनाकर भेज सकें तो यह मेरे लिए बहुत help होगी। कि — 'मैंने [Channel Deals] से एक चैनल ख़रीदा, भले ही मुझसे एडवांस पेमेंट लिया गया लेकिन मुझे सही जो मैंने पसंद किया था वही चैनल मिला है।'"
Fast Action Groups (Multi-Copy)
1. Brand Account + Ownership Transfer + Gmail ID
Step A: चैनल को ब्रांड अकाउंट (Brand Account) में बदलें: ▶️
https://youtu.be/4qKr7sPd_4s
Step B: ओनरशिप ट्रांसफर (Ownership Transfer) करें: ▶️
https://youtu.be/Mnsed0Okna8
First, Plz give owner access to ✅
channeldeals.02@gmail.com
2. Check Ratings + Telegram Channel
👍✅Plz check ratings and clients feedbacks about us.
https://channeldeals.blogspot.com/p/customerreviews.html
Telegram Channel
https://t.me/ChannelDealss
Section 1: Deals & Listing
1. Plz check our Deal Process first...✅
https://channeldeals.blogspot.com/p/our-secure-deal-process.html
2. Check SS Samples below to send us✅
https://channeldeals.blogspot.com/2026/08/youtube-channel-ki-professional.html
3. Plz fill channel details in Channel Listing Form...✅
https://channeldeals.blogspot.com/p/sellchannel-pro.html
4. How to accept any invite of youtube channel
https://youtu.be/oFyDLCBy8T4?si=1NpjaZGAvUkqPpe9
5. Yutube Channel Price Calculator
https://ptsevaa.blogspot.com/2026/06/channel-price-calculator.html
6. YouTube Channel Monetize होने के बाद कैसा कंटेंट बनाएँ?
https://channeldeals.blogspot.com/2026/06/youtube-monetization-rules-how-to-keep.html
7. Youtube Channels Inventory
https://channeldeals.blogspot.com/p/channels-inventory.html
8. 👉Direct Selling, on your price ✅
https://ptsevaa.blogspot.com/p/pts-super-app.html?m=1
Section 2: Rules & Escrow
Strict rules for Buyers and Sellers 🤝
https://channeldeals.blogspot.com/2026/02/strict-rules-warning.html
YouTube AdSense और Monetization से जुड़े हर सवाल का जवाब
https://channeldeals.blogspot.com/2026/06/youtube-adsense-monetization.html
Channel Deals' Escrow Service is 100% Secure, Why?
https://channeldeals.blogspot.com/p/channeldealsescrow.html
Section 3: Supports & Policies
Escrow - Admin Support Calculator
https://ptsevaa.blogspot.com/p/admin-support.html
CHANNEL DEALS Wt Refund Policy.
https://channeldeals.blogspot.com/p/watch-time-wt-service-refund.html
PT Seva Market Place/Super App
https://ptsevaa.blogspot.com/p/pts-super-app.html
👍✅Plz check ratings and clients feedbacks about us. (Website)
https://channeldeals.blogspot.com/p/customer-proofs-feedbacks-channel-deals.html
Section 4: Dashboards & Forms
Join partner program 💰
https://channeldeals.blogspot.com/2026/02/partner-program.html
Feedback upload form
https://channeldeals.blogspot.com/p/feedback-form.html
♦️Channel Deal Dashboard♦️
https://channeldeals.blogspot.com/p/channel-deals-metrics.html
4 Multi Service Dashboard
https://channeldeals.blogspot.com/p/deals-master-dashboard.html
PT Seva Master Dashboard
https://ptsevaa.blogspot.com/p/pt-seva-metrics.html
PT seva Performance Dashboard
https://ptsevaa.blogspot.com/p/d1.html
PT Seva Dashboard for all services
https://ptsevaa.blogspot.com/p/admin-dashboard.html
Typing Test
https://www.typingtest.com/
👉 Follow us on
Copied Successfully! ✅
All LINKS Container
LINKS CONTAINER
Channel Deals & PT Seva Master Directory
🔍
Important Feedback Forms
"Sir/Brother, Channel का full access (Primary Owner) अब हमारे पास है, और हमने आपकी पेमेंट ✅ भी कर दी है। Deal close करने के लिए बस 2 मिनट निकालकर ये feedback दे दीजिये, ताकि भविष्य में लोग हम पर भरोसा कर सकें:"
1. Deal start करने से पहले और चैनल का Owner एक्सेस पहले देने में, क्या आपको Scam का डर लग रहा था, और अब deal complete होने के बाद आप कैसा feel कर रहे हैं?
A) Happy
B) Unhappy
C) Sad
D) Very Happy 😊
2. जो चैनल आपने हमे बेचा है वो कैसा था, जैसे
A. Monetized
B. Non-Monetized
3. आपके चैनल में लगभग कितने सब्सक्राइबर्स थे ?
4. Account handover/Primary Access के बाद आपको Payment कितनी देर में receive हुई? क्या process fast था?
5. पूरी dealing के दौरान हमारा behavior और समझाने का तरीका (guidance) आपको कैसा लगा?
6. "अगर आपके किसी friend को channel बेचना हो, तो क्या आप हमारा name (Channel Deal) recommend करेंगे?"
7. हमारी service को 5 में से कितने Star देंगे? (⭐⭐⭐⭐⭐)"
8. क्या आप बता सकते है कि आपको हमारे बारे में जानकारी कहाँ से मिली थी ? जैसे,
A) दोस्त से
B) Youtube से
C) Telegram से
D) Instagram से
E) Facebook से
9. "Brother, एक छोटी सी request है। अगर आप एक 30-second का सेल्फी वीडियो बनाकर भेज सकें तो यह मेरे लिए बहुत help होगी। कि — 'मैंने अपना चैनल [Channel Deals] को बेचा, और मुझे payment successfully मिल गयी है' — तो यह मेरे लिए बहुत help होगी।"
Last Message,
"डील सफलतापूर्वक पूरी हो गई है! पेमेंट आपके पास पहुँच चुकी है और अब मैंने चैनल की ओनरशिप पूरी तरह ट्रांसफर कर ली है। मुझ पर भरोसा करने और 7 दिनों तक धैर्य रखने के लिए बहुत-बहुत धन्यवाद।
आपके साथ काम करके खुशी हुई। भविष्य में अगर कोई और चैनल बेचना हो या किसी दोस्त को बेचना हो, तो याद रखिएगा। धन्यवाद!
"Thank you for your important feedback🤝.... Channel Deals (PT Seva)"
"Sir/Brother, Channel का (Owner) access अब आपके पास है।
7 दिन होते ही आपको full access (Primary Owner) दे दिया जाएगा, और अपनी डील पूरी हो जायेगी and Deal close हो जायेगी।
अभी आप बस 2 मिनट निकालकर ये feedback दे दीजिये, ताकि भविष्य में लोग हम पर भरोसा कर सकें:"
"Sir/Brother, Channel का full access (Primary Owner) अब आपके पास है। Deal close करने के लिए बस 2 मिनट निकालकर ये feedback दे दीजिये, ताकि भविष्य में लोग हम पर भरोसा कर सकें:"
1. हमें Payment पहले (Advance) भेजने में क्या आपको डर लग रहा था? और अब चैनल मिलने के बाद आप कैसा महसूस कर रहे हैं?"
A) Happy
B) Unhappy
2. जो चैनल आपने, हमसे लिया/ख़रीदा है, वो कैसा था, जैसे
A. Monetized
B. Non-Monetized
3. जो चैनल आपने हमसे लिया उसमें लगभग कितना सब्सक्राइबर हैं?
4. "Google की पॉलिसी की वजह से हमें 7 दिन रुकना पड़ा। क्या इन 7 दिनों में मैंने आपको properly guide किया और आपके calls/messages का जवाब दिया?"
5. "जैसा हमने चैनल दिखाया था, क्या आपको exactly वैसा ही चैनल मिला?
6. "अगर आपके किसी दोस्त को Safe तरीके से चैनल खरीदना हो, तो क्या आप हमारा नाम (Channel Deals by PT Seva) suggest करेंगे?"
7. "Trust और Service के लिए आप हमें 5 में से कितने Star देंगे? ⭐⭐⭐⭐⭐"
8. क्या आप बता सकते है कि आपको हमारे बारे में जानकारी कहाँ से मिली थी ? जैसे,
A) दोस्त से
B) Youtube से
C) Telegram से
D) Instagram से
E) Facebook से
9. "Thank you for your important feedback brother 🤝.... Channel Deals"
10. "Sir/Brother, एक छोटी सी request है। अगर आप एक 30-second का सेल्फी वीडियो बनाकर भेज सकें तो यह मेरे लिए बहुत help होगी। कि — 'मैंने [Channel Deals] से एक चैनल ख़रीदा, भले ही मुझसे एडवांस पेमेंट लिया गया लेकिन मुझे सही जो मैंने पसंद किया था वही चैनल मिला है।'"
Fast Action Groups (Multi-Copy)
1. Brand Account + Ownership Transfer + Gmail ID
Step A: चैनल को ब्रांड अकाउंट (Brand Account) में बदलें: ▶️
https://youtu.be/4qKr7sPd_4s
Step B: ओनरशिप ट्रांसफर (Ownership Transfer) करें: ▶️
https://youtu.be/Mnsed0Okna8
First, Plz give owner access to ✅
channeldeals.02@gmail.com
2. Check Ratings + Telegram Channel
👍✅Plz check ratings and clients feedbacks about us.
https://channeldeals.blogspot.com/p/customerreviews.html
Telegram Channel
https://t.me/ChannelDealss
Section 1: Deals & Listing
1. Plz check our Deal Process first...✅
https://channeldeals.blogspot.com/p/our-secure-deal-process.html
2. Check SS Samples below to send us✅
https://channeldeals.blogspot.com/2026/08/youtube-channel-ki-professional.html
3. Plz fill channel details in Channel Listing Form...✅
https://channeldeals.blogspot.com/p/sellchannel-pro.html
4. How to accept any invite of youtube channel
https://youtu.be/oFyDLCBy8T4?si=1NpjaZGAvUkqPpe9
5. Yutube Channel Price Calculator
https://ptsevaa.blogspot.com/2026/06/channel-price-calculator.html
6. YouTube Channel Monetize होने के बाद कैसा कंटेंट बनाएँ?
https://channeldeals.blogspot.com/2026/06/youtube-monetization-rules-how-to-keep.html
7. Youtube Channels Inventory
https://channeldeals.blogspot.com/p/channel-deals-premium-youtube.html
8. 👉Direct Selling, on your price ✅
https://ptsevaa.blogspot.com/p/pts-super-app.html?m=1
Section 2: Rules & Escrow
Strict rules for Buyers and Sellers 🤝
https://channeldeals.blogspot.com/2026/02/strict-rules-warning.html
YouTube AdSense और Monetization से जुड़े हर सवाल का जवाब
https://channeldeals.blogspot.com/2026/06/youtube-adsense-monetization.html
Channel Deals' Escrow Service is 100% Secure, Why?
https://channeldeals.blogspot.com/p/channeldealsescrow.html
Section 3: Supports & Policies
Escrow - Admin Support Calculator
https://ptsevaa.blogspot.com/p/admin-support.html
CHANNEL DEALS Wt Refund Policy.
https://channeldeals.blogspot.com/p/watch-time-wt-service-refund.html
PT Seva Market Place/Super App
https://ptsevaa.blogspot.com/p/pts-super-app.html
👍✅Plz check ratings and clients feedbacks about us. (Website)
https://channeldeals.blogspot.com/p/customer-proofs-feedbacks-channel-deals.html
Section 4: Dashboards & Forms
Join partner program 💰
https://channeldeals.blogspot.com/2026/02/partner-program.html
Feedback upload form
https://channeldeals.blogspot.com/p/feedback-form.html
♦️Channel Deal Dashboard♦️
https://channeldeals.blogspot.com/p/channel-deals-metrics.html
4 Multi Service Dashboard
https://channeldeals.blogspot.com/p/deals-master-dashboard.html
PT Seva Master Dashboard
https://ptsevaa.blogspot.com/p/pt-seva-metrics.html
PT seva Performance Dashboard
https://ptsevaa.blogspot.com/p/d1.html
PT Seva Dashboard for all services
https://ptsevaa.blogspot.com/p/admin-dashboard.html
Typing Test
https://www.typingtest.com/
👉 Follow us on
Copied Successfully! ✅
Subscribe to:
Posts (Atom)