The item list loads automatically from the Off The Rails Google Sheet. Use this screen to optionally connect the shared orders backend (Apps Script) so all chefs' orders pool into one live list.
Apps Script Setup (one-time)
In your Google Sheet go to Extensions → Apps Script, paste this code, click Deploy → New deployment → Web app (Anyone can access), copy the URL above.
// Paste into Google Apps Script
const RESET_HOUR = 9;
function doGet(e) {
const action = e.parameter.action || '';
const sheetName = e.parameter.sheet || 'OTR Daily Order';
const curName = e.parameter.current || 'OTR Current Order';
if (action === 'saveproduct') {
return saveProduct(e.parameter);
}
if (action === 'read') {
return readCurrentOrder(curName);
}
if (action === 'clear') {
clearCurrentOrder(curName);
return ContentService.createTextOutput('ok');
}
if (action === 'reset') {
copyToHistory(curName, sheetName);
clearCurrentOrder(curName);
return ContentService.createTextOutput('ok');
}
if (action === 'deleteline') {
const sku = e.parameter.sku || '';
deleteFromCurrent(sku, curName);
return ContentService.createTextOutput('ok');
}
if (action === 'editline') {
const sku = e.parameter.sku || '';
const sup = e.parameter.sup || '';
const name = e.parameter.name || '';
const qty = e.parameter.qty || '';
const time = e.parameter.time || '';
const chef = e.parameter.chef || 'EDIT';
deleteFromCurrent(sku, curName);
const sh = getCurrentSheet(curName);
sh.appendRow([getTodayKey(), chef, time, sup, name, sku, qty]);
return ContentService.createTextOutput('ok');
}
return ContentService.createTextOutput('ok');
}
function doPost(e) {
const data = JSON.parse(e.postData.contents);
const curName = data.currentOrder || 'OTR Current Order';
return writeOrder(data, curName);
}
function getTodayKey() {
const now = new Date();
if (now.getHours() < RESET_HOUR) now.setDate(now.getDate() - 1);
return Utilities.formatDate(now, 'GMT', 'yyyy-MM-dd');
}
function getSheet(name) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
let sh = ss.getSheetByName(name);
if (!sh) {
sh = ss.insertSheet(name);
sh.appendRow(['Date','Chef','Time','Supplier','Item','SKU','Qty']);
}
return sh;
}
function getCurrentSheet(curName) { return getSheet(curName); }
function readCurrentOrder(curName) {
const sh = getCurrentSheet(curName);
const today = getTodayKey();
const rows = sh.getDataRange().getValues();
const items = [];
for (let i = 1; i < rows.length; i++) {
if (String(rows[i][0]) === today) {
items.push({
chef: rows[i][1], time: rows[i][2],
supplier: rows[i][3], name: rows[i][4],
sku: rows[i][5], qty: rows[i][6]
});
}
}
const json = JSON.stringify({ ok: true, items });
return ContentService.createTextOutput(json)
.setMimeType(ContentService.MimeType.JSON);
}
function writeOrder(data, curName) {
const sh = getCurrentSheet(curName);
const today = getTodayKey();
data.items.forEach(item => {
sh.appendRow([today, data.chef, data.time,
item.supplier, item.name, item.sku, item.qty]);
});
return ContentService.createTextOutput('ok');
}
function deleteFromCurrent(sku, curName) {
const sh = getCurrentSheet(curName);
const today = getTodayKey();
const rows = sh.getDataRange().getValues();
for (let i = rows.length - 1; i >= 1; i--) {
if (String(rows[i][0]) === today && String(rows[i][5]) === String(sku)) {
sh.deleteRow(i + 1);
}
}
}
function clearCurrentOrder(curName) {
const sh = getCurrentSheet(curName);
const today = getTodayKey();
const rows = sh.getDataRange().getValues();
for (let i = rows.length - 1; i >= 1; i--) {
if (String(rows[i][0]) === today) sh.deleteRow(i + 1);
}
}
function copyToHistory(curName, histName) {
const cur = getCurrentSheet(curName);
const hist = getSheet(histName);
const today = getTodayKey();
const rows = cur.getDataRange().getValues();
for (let i = 1; i < rows.length; i++) {
if (String(rows[i][0]) === today) {
hist.appendRow(rows[i]);
}
}
}
// Auto-reset at 9am via time-based trigger
// Set up: Triggers -> Add Trigger -> dailyReset -> Time-driven -> Day timer -> 9am
function saveProduct(p) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const venue = p.venue || 'otr';
const tabName = venue === 'lp' ? 'LP Order Sheet' : 'OTR Order Sheet';
let sh = ss.getSheetByName(tabName);
if (!sh) sh = ss.getSheetByName('order sheet'); // fallback
if (!sh) return ContentService.createTextOutput('no sheet');
const rowIndex = parseInt(p.rowIndex);
const rowData = [p.name, p.sku, p.supplier, '', p.type, p.foh, '', '', p.price, '', p.section];
if (rowIndex >= 0) {
// Edit existing — find row by SKU
const rows = sh.getDataRange().getValues();
for (let i = 1; i < rows.length; i++) {
if (String(rows[i][1]) === String(p.sku)) {
sh.getRange(i + 1, 1, 1, rowData.length).setValues([rowData]);
return ContentService.createTextOutput('updated');
}
}
}
// Add new row
sh.appendRow(rowData);
return ContentService.createTextOutput('added');
}
function dailyReset() {
const venues = [
{ current: 'OTR Current Order', history: 'OTR Daily Order' },
{ current: 'LP Current Order', history: 'LP Daily Order' }
];
venues.forEach(v => {
copyToHistory(v.current, v.history);
clearCurrentOrder(v.current);
});
}