商品一覧 TOP 5(原価順)

検索ワードを入力してください

例:小麦 / 乳 / 卵 / 落花生

📗 レシピを取り込む
📗
タップしてExcelを選択
.xlsx / .xls — パーツ+商品を自動で取り込み
※見出し行に「材料」(または「原料」「食材」)と「重量」の列が必要です
📄
タップしてCSVを選択
材料マスタに登録済みの材料名で記入
🥕 材料を取り込む
📊
タップしてExcelを選択
材料名・重量・価格の列を自動検出
📄
タップしてCSVを選択
名前・単位重量・単位価格の3列形式
☁️ スプレッドシート連携
📋 事前準備:ウェブに公開してください
1スプレッドシートを開き、「ファイル」→「共有」→「ウェブに公開」をクリック
2「リンク」タブで取り込みたいシートを選び「公開」ボタンを押す
3元のスプレッドシートのアドレスバーのURL(/editのやつ)をコピーして下に貼り付け
https://docs.google.com/spreadsheets/d/← ここのURL/edit
読み込むデータを選んでください
📦 材料のタブ名
📋 レシピのタブ名
🎂 商品のタブ名

ツールのデータをスプレッドシートに書き出します。
書き込みにはGASのデプロイが必要です。

🛠️ 事前準備:GASのデプロイ(初回のみ)
1スプレッドシートを開き、「拡張機能 › Apps Script」をクリック
2下の「スクリプトをコピー」を押してスクリプトを貼り付けて保存
3「デプロイ › 新しいデプロイ」→ ウェブアプリ → アクセス:全員 → デプロイ
4表示されたURLを⚙️設定タブの「GAS Web App URL」(スプレッドシートに書き込むための接続URL)に貼り付け
📸 価格改定(写真を見ながら打ち替え)

仕入れ先からの値上げのお知らせ(写真)を横に表示して、見ながら価格を打ち替えられます。正式名称を登録した材料が一覧に出ます。
※写真を自動で読み取る機能ではありません。

📷
写真を見ながら価格を打ち替える
タップして写真を選択(JPG・PNG など)
🔧 材料とレシピのつながりを直す

取り込みで材料の名前が一致しなかったところを、名前でつなぎ直します。Excel・CSV・スプレッドシートから取り込んだあとは、いちど押しておくと安心です。

名前 内容量 価格 1gあたりの値段 アレルゲン
🎂 ここはホールケーキ1台の原価を出す場所。売り物として値段を付けて利益率を見るのは、ホームの「商品」です。
Googleスプレッドシート連携
シート名(空欄でデフォルト名)
最終同期:なし
GASスクリプト(コピー用)
スプレッドシートを開き、「拡張機能 › Apps Script」(スプレッドシートに付いているプログラム置き場)にこのスクリプトを貼り付け、「デプロイ › 新しいデプロイ › ウェブアプリ(アクセス:全員)」で出てきたURLを上の「GAS Web App URL」に貼ってください。
// ==========================================
// 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);
}
アカウント
ログイン中:
プラン:
バックアップ
データはこのブラウザの中に保存されています(別の端末には自動で同期されません)。月に1回はバックアップファイルを保存しておくと安心です。
データ削除
新規レシピ
材料を全部混ぜて、できあがった生地・クリーム全体の重さ(g)。※これが無いと原価が計算できません
レシピ原価 ¥0.00
1gあたりの値段 ¥0.000
新規商品
🏷️ ここは販売価格を付けて利益率を見る場所。パーツ(レシピ)を組み合わせて1個ぶんの原価を出し、売価と比べます。
商品原価 ¥0.00
利益率
利益額
新規ケーキレシピ
ホール原価 ¥0.00
1gあたりの値段