検索ワードを入力してください
例:小麦 / 乳 / 卵 / 落花生
ツールのデータをスプレッドシートに書き出します。
書き込みにはGASのデプロイが必要です。
仕入れ先からの値上げのお知らせ(写真)を横に表示して、見ながら価格を打ち替えられます。正式名称を登録した材料が一覧に出ます。
※写真を自動で読み取る機能ではありません。
取り込みで材料の名前が一致しなかったところを、名前でつなぎ直します。Excel・CSV・スプレッドシートから取り込んだあとは、いちど押しておくと安心です。
| 名前 | 内容量 | 価格 | 1gあたりの値段 | アレルゲン |
|---|
// ==========================================
// withdo 原価計算ツール — Google Apps Script
// ==========================================
// デプロイ方法:
// 1. スプレッドシートを開く
// 2. 拡張機能 > Apps Script
// 3. 以下をコピペして保存
// 4. デプロイ > 新しいデプロイ > ウェブアプリ
// 実行ユーザー:自分 / アクセス:全員
// 5. デプロイ後のURLを設定に貼り付ける
const SHEET_NAMES = {
materials: '材料',
recipes: 'レシピ',
products: '商品',
};
function doGet(e) {
const action = e.parameter.action;
if (action === 'ping') return json({ status: 'ok' });
if (action === 'read') {
const data = readSheet(e.parameter.sheet);
return json({ status: 'ok', data });
}
if (action === 'verify') {
const result = verifyLicense(e.parameter.email, e.parameter.key);
return json(result);
}
return json({ status: 'error', message: 'unknown action' });
}
// ==========================================
// ライセンス照合
// licensesシート形式:
// A: email
// B: key
// C: active (Y / N)
// D: plan (single_1 / single_2 / pack_all / sub_monthly など)
// E: gasUrl (購入者のGAS WebアプリURL)
// F: 購入者名
// G: 購入日
// ==========================================
function verifyLicense(email, key) {
try {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sh = ss.getSheetByName('licenses');
if (!sh) return { status: 'error' };
const rows = sh.getDataRange().getValues();
for (let i = 1; i < rows.length; i++) {
const rowEmail = String(rows[i][0]).toLowerCase().trim();
const rowKey = String(rows[i][1]).trim();
const rowActive = String(rows[i][2]).toUpperCase().trim();
const rowPlan = String(rows[i][3]).trim() || 'single_2';
const rowGasUrl = String(rows[i][4]).trim();
if (rowEmail === email.toLowerCase().trim() && rowKey === key) {
if (rowActive !== 'Y') return { status: 'inactive' };
return { status: 'ok', plan: rowPlan, gasUrl: rowGasUrl };
}
}
return { status: 'invalid' };
} catch(e) {
return { status: 'error' };
}
}
function doPost(e) {
const { action, sheet, data } = JSON.parse(e.postData.contents);
if (action === 'write') {
writeSheet(sheet, data);
return json({ status: 'ok' });
}
return json({ status: 'error', message: 'unknown action' });
}
function readSheet(sheetName) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sh = ss.getSheetByName(sheetName);
if (!sh) return [];
const rows = sh.getDataRange().getValues();
if (rows.length < 2) return [];
const headers = rows[0];
return rows.slice(1).map(row => {
const obj = {};
headers.forEach((h, i) => { obj[h] = row[i]; });
['aliases','allergens','items','parts'].forEach(key => {
if (obj[key] && typeof obj[key] === 'string') {
try { obj[key] = JSON.parse(obj[key]); } catch(e) {}
}
});
return obj;
});
}
function writeSheet(sheetName, data) {
if (!data || !data.length) return;
const ss = SpreadsheetApp.getActiveSpreadsheet();
let sh = ss.getSheetByName(sheetName);
if (!sh) sh = ss.insertSheet(sheetName);
sh.clearContents();
const headers = Object.keys(data[0]);
const rows = data.map(obj =>
headers.map(h => {
const v = obj[h];
if (Array.isArray(v)) return v.length ? JSON.stringify(v) : '';
if (v && typeof v === 'object') return JSON.stringify(v);
return v ?? '';
})
);
sh.getRange(1, 1, 1, headers.length).setValues([headers]);
if (rows.length > 0)
sh.getRange(2, 1, rows.length, headers.length).setValues(rows);
}
function json(obj) {
return ContentService
.createTextOutput(JSON.stringify(obj))
.setMimeType(ContentService.MimeType.JSON);
}