diff --git a/public/sim/serving-robot.html b/public/sim/serving-robot.html index 547448c..6b69a93 100644 --- a/public/sim/serving-robot.html +++ b/public/sim/serving-robot.html @@ -241,0 +242,2 @@ footer { color: var(--ink-3); font-size: 11px; } + + ロボットの費用と変わること(ひと月)は下にあります ↓ @@ -280,0 +283,77 @@ footer { color: var(--ink-3); font-size: 11px; } +
+

ロボットの費用と、変わること(ひと月)

+

+ +
+
+ +

人件費:店が回る最少のスタッフは何人減るか

+
+

+

人手不足:スタッフの手がふさがっている割合(9割を超えると呼び出しに出られない)/作業の軽さ:スタッフ1人が歩く距離/サービス:入店待ちと、料理ができてから届くまで

+
+

+ +
+ 人件費のほかに、ロボットを入れて良くなること +
    +
  • 人手不足:配膳の往復をロボットが受け持つ分、スタッフの手が空き、人が足りない日でも案内・会計・呼び出しに出やすくなる(2つめの表の「手がふさがっている割合」)
  • +
  • 作業の軽さ:厨房と客席の往復が減り、歩く距離が短くなる。下げ膳にも使えば、重い皿を運ぶ負担も減る(「1人が歩く距離」)
  • +
  • サービス:料理が早く届き、入店待ちも縮む。空いた手を、客と話す・様子を見るといった人にしかできない接客に回せる(「入店待ち」「料理が届くまで」)
  • +
  • 試算に入れていないこと:採用・教育の手間、人が辞めたときの穴の埋めやすさ、スタッフの疲れ方。数字にはしていませんが、店によっては大きい点です
  • +
+
+ 費用の前提を選ぶ +
+
+ ロボットの払い方 +
+
+
+ 1台の値段 +
+

+
+
+ 保守(1台・月) +
+

修理・点検の契約。月額に含まれる契約もあります。

+
+
+ 使う年数 +
+

買った値段を、使う月数で割ってひと月の費用にします。

+
+
+ 補助金 +
+

中小企業省力化投資補助金(カタログ注文型)。買う値段の1/2まで。下の「費用の前提と補助金」に条件があります。

+
+
+ スタッフの時給(円) +
+

1,177円は、2026年度の最低賃金の全国平均(答申)です。

+
+
+ 1人減らすと浮く時間(ピーク1回) +
+

「ピークの間」は2〜2.5時間です。ピークの間だけ人を減らせるとは限らず、シフトは数時間単位で組むことが多いため、4時間も選べます。

+
+
+ ピークの回数 +
+

ひと月は30日で数えます。

+
+
+
+
+ 費用の前提と補助金(2026年10月に調べた値) + +

この費用の試算は、上の試算の結果(店が回るかどうか)をそのまま使っています。上の試算の数字は変えていません。人件費は「店が回る最少のスタッフ」がロボットで何人減るかだけから出していて、人手不足・作業の軽さ・サービスの変化は、2つめの表に試算の結果をそのまま並べていて、お金には直していません。

+

まだ入れていないもの:地図づくり・設定・スタッフへの説明にかかる初めの費用、故障で止まっている間の手間、電気代以外の消耗品、補助金の申請の手間と通るかどうか、税金(買ったときの減価償却の扱い)。

+
+
+ @@ -573,0 +653 @@ function render() { + renderCost(); @@ -670,0 +751,178 @@ function renderAssume() { +// ロボットの費用と、浮く人件費(2026-10-23 追加)。上の試算の結果(店が回るか)をそのまま使い、数字は変えない +// 人件費の差:ロボット n 台で「店が回る最少のスタッフ」が、ロボットなしより何人減るか × 浮く時間 × 時給 +const COST = { + pay: "rent", price: { rent: 60000, buy: 2500000 }, maint: 10000, years: 5, sub: false, + wage: 1177, hours: "peak", peaks: 2, +}; +const COST_DAYS = 30; // ひと月の営業日 +const COST_CHARGE = 2000; // 充電の電気代(1台・月)。公開されている目安 1,000〜3,000円の真ん中 +const COST_SHIFT_H = 4; // 「シフト1回分」で浮くと数える時間 +const SUB_RATE = 0.5, SUB_CAP = 7500000; // 省力化投資補助金(カタログ注文型):1/2以下・従業員6〜20人の上限750万円 +const PRICE_OPTS = { + rent: [30000, 60000, 100000], + buy: [1500000, 2500000, 3500000], +}; +const man = v => { + if (Math.round(v / 1000) === 0) return "0円"; + const a = Math.abs(v) / 10000, r = Math.round(a * 10) / 10, t = a >= 100 ? Math.round(a).toLocaleString("ja-JP") : (Number.isInteger(r) ? String(r) : r.toFixed(1)); + return `${v < 0 ? "−" : ""}${t}万円`; +}; +// 左の条件のままで、ロボット n 台のとき店が回る最少のスタッフ(選べる人数の範囲で探す) +function minStaff(n) { + const s = S(), arr = arrN(); + for (const st of s.staff_all) { + const r = find(state.store, st, n, arr, state.mode); + if (r && !unstable(r)) return { st, r }; + } + return null; +} +function robotCost(n) { + const p = COST.price[COST.pay]; + const run = n * (COST.maint + COST_CHARGE); + if (COST.pay === "rent") return { month: n * p + run, run, first: 0, subsidy: 0 }; + const total = n * p, subsidy = COST.sub ? Math.min(total * SUB_RATE, SUB_CAP) : 0; + const first = total - subsidy; + return { month: first / (COST.years * 12) + run, run, first, subsidy }; +} +function hoursSaved() { return COST.hours === "peak" ? S().peak_min / 60 : COST_SHIFT_H; } +function buildCost() { + const yen = v => v.toLocaleString("ja-JP"); + seg("seg-cpay", "cpay", [{ v: "rent", l: "月額で借りる" }, { v: "buy", l: "買う" }], () => COST.pay, v => { COST.pay = v; buildCost(); }); + const po = PRICE_OPTS[COST.pay]; + seg("seg-cprice", "cprice", po.map(v => ({ v, l: `${v / 10000}万円` })), () => COST.price[COST.pay], v => COST.price[COST.pay] = v); + document.getElementById("cprice-legend").textContent = COST.pay === "rent" ? "1台の月額" : "1台の値段"; + document.getElementById("cprice-note").textContent = COST.pay === "rent" + ? "レンタル・リースの相場は月3〜12万円(中央値は6万円台)。" + : "買う場合の相場は150〜350万円(中央値は250万円前後)。"; + seg("seg-cmaint", "cmaint", [{ v: 0, l: "込み" }, { v: 10000, l: "1万円" }, { v: 20000, l: "2万円" }], () => COST.maint, v => COST.maint = v); + seg("seg-cyears", "cyears", [3, 5, 7].map(v => ({ v, l: `${v}年` })), () => COST.years, v => COST.years = v); + seg("seg-csub", "csub", [{ v: false, l: "使わない" }, { v: true, l: "使う" }], () => COST.sub, v => COST.sub = v); + document.getElementById("ctl-cyears").hidden = COST.pay !== "buy"; + document.getElementById("ctl-csub").hidden = COST.pay !== "buy"; + seg("seg-cwage", "cwage", [1100, 1177, 1300, 1500].map(v => ({ v, l: yen(v) })), () => COST.wage, v => COST.wage = v); + seg("seg-chours", "chours", [{ v: "peak", l: "ピークの間" }, { v: "shift", l: `${COST_SHIFT_H}時間` }], () => COST.hours, v => COST.hours = v); + seg("seg-cpeaks", "cpeaks", [{ v: 1, l: "1日1回" }, { v: 2, l: "1日2回" }], () => COST.peaks, v => COST.peaks = v); +} +function renderCost() { + const s = S(), arr = arrN(); + const h = hoursSaved(), perPerson = h * COST.peaks * COST_DAYS * COST.wage; + document.getElementById("cost-sub").textContent = + `${s.label}・${CROWD[state.crowd]}(${arr}組/時)・${MODE_SHORT[state.mode]}・${state.trays === 1 ? "1卓ずつ" : "最大3卓"}は、左の条件と同じです。` + + `ロボットの台数ごとに、店が回る最少のスタッフが何人減るか(人件費)と、人数をそのままにしたときの人手不足・作業の軽さ・サービスの変化を、ロボットの費用と並べます。`; + const maxR = Math.max(3, state.robots); + const base = minStaff(0); + const rows = ROBOTS_ALL.filter(n => n <= maxR).map(n => { + const m = minStaff(n), c = robotCost(n); + const saved = base && m ? base.st - m.st : null; + const labor = saved === null ? null : saved * perPerson; + return { n, m, c, saved, labor, net: labor === null ? null : labor - c.month }; + }); + const nets = rows.filter(x => x.n > 0 && x.net !== null).map(x => x.net); + const bestNet = nets.length ? Math.max(...nets) : null; + const head = `ロボット店が回る最少のスタッフそのときの入店待ちロボットの費用浮く人件費差し引き`; + const body = rows.map(x => { + const cur = x.n === state.robots ? " cur" : ""; + if (!x.m) return `${x.n}台選べる人数(${s.staff_all[0]}〜${s.staff_all[s.staff_all.length - 1]}人)では店が回りません`; + const first = COST.pay === "buy" && x.n > 0 + ? `初めに ${man(x.c.first)}${x.c.subsidy ? `(補助 ${man(x.c.subsidy)})` : ""}` : ""; + const lab = x.n === 0 ? "基準" : x.saved > 0 ? `${man(x.labor)}${x.saved}人分` : x.saved === 0 ? `0円人数は同じ` : `${man(x.labor)}${-x.saved}人増える`; + const net = x.n === 0 ? "—" : man(x.net); + return `${x.n}台${x.m.st}人${plainMin(x.m.r.entry_wait_mean)}` + + `${x.n === 0 ? "0円" : man(x.c.month)}${first}${lab}` + + `${net}`; + }).join(""); + document.getElementById("costtable").innerHTML = head + `${body}`; + + // 人数をそのままにしてロボットを入れたとき(左のスタッフ数):人手不足・作業の軽さ・サービス + const staff = state.staff; + document.getElementById("cost2-h").textContent = `人数をそのまま(スタッフ${staff}人)にしてロボットを入れたとき`; + const same = ROBOTS_ALL.filter(n => n <= maxR).map(n => ({ n, r: find(state.store, staff, n, arr, state.mode), c: robotCost(n) })); + const r0 = same[0].r; + const diff = (a, b) => { const d = a ? b / a - 1 : 0; return Math.abs(d) < 0.005 ? "" : `${d < 0 ? "−" : "+"}${Math.abs(Math.round(d * 100))}%`; }; + const head2 = `ロボットロボットの費用手がふさがっている割合1人が歩く距離入店待ち料理が届くまで`; + const body2 = same.map(({ n, r, c }) => { + if (!r) return ""; + const cur = n === state.robots ? " cur" : ""; + return `${n}台${n === 0 ? "0円" : man(c.month)}` + + `${plainPct(r.staff_util)}${r.walk_km_per_staff.toFixed(2)}km${n ? diff(r0.walk_km_per_staff, r.walk_km_per_staff) : ""}` + + `${seatCell(r)}${plainSec(r.dish_wait_mean)}`; + }).join(""); + document.getElementById("costtable2").innerHTML = head2 + `${body2}`; + + // 左で選んでいる台数についての一文 + const n = state.robots, x = rows.find(y => y.n === n); + let v; + if (!base) v = `ロボットなしでは、選べる人数の範囲で店が回らないので、減らせる人数を比べられません。左の混み具合を下げると比べられます。`; + else if (n === 0) v = `左でロボットを1台以上選ぶと、その台数の費用と、浮く人件費をここにまとめます。ロボットなしで店が回る最少のスタッフは${base.st}人です。`; + else if (!x || !x.m) v = `ロボット${n}台でも、選べる人数の範囲では店が回りません。`; + else { + const wait = `入店待ちは ${plainMin(base.r.entry_wait_mean)} → ${plainMin(x.m.r.entry_wait_mean)}`; + const cost = COST.pay === "rent" ? `ロボットの費用は月${man(x.c.month)}` : `ロボットの費用は、初めに${man(x.c.first)}${x.c.subsidy ? `(補助金${man(x.c.subsidy)}を引いた額)` : ""}、${COST.years}年で割ると月${man(x.c.month)}`; + if (x.saved > 0) { + const back = COST.pay === "buy" && x.labor > x.c.run ? `。初めに払う額は、およそ${Math.ceil(x.c.first / (x.labor - x.c.run))}か月で取り戻せる計算です` : ""; + v = `ロボット${n}台なら、店が回る最少のスタッフは${base.st}人 → ${x.m.st}人です(${wait})。浮く人件費は月${man(x.labor)}、${cost}で、差し引き月${man(x.net)}${back}。` + + (x.net < 0 ? `人件費だけでは元が取れないので、待ち時間や料理が届く早さの良さ(上の表)と合わせて判断する形になります。` : ""); + } else if (x.saved === 0) { + const sr = same.find(y => y.n === n), a0 = r0, b0 = sr && sr.r; + const walk = b0 ? Math.round((1 - b0.walk_km_per_staff / a0.walk_km_per_staff) * 100) : 0; + const gains = b0 ? `スタッフ${staff}人のままなら、手がふさがっている割合は ${plainPct(a0.staff_util)} → ${plainPct(b0.staff_util)}、1人が歩く距離は${walk >= 0 ? `${walk}%減り` : `${-walk}%増え`}、入店待ちは ${plainMin(a0.entry_wait_mean)} → ${plainMin(b0.entry_wait_mean)}、料理が届くまでは ${plainSec(a0.dish_wait_mean)} → ${plainSec(b0.dish_wait_mean)} です。` : ""; + v = `ロボット${n}台を入れても、店が回る最少のスタッフは${base.st}人のままで、人件費は浮きません。${cost}で、変わるのは人手不足・作業の軽さ・サービスの方です。` + gains; + } else { + v = `ロボット${n}台では、店が回る最少のスタッフがかえって増えます(${base.st}人 → ${x.m.st}人)。`; + } + if (n >= 5) v += ROBOT_NOTE; + } + document.getElementById("cost-verdict").textContent = v; + + // 欄の頭のカード3枚(左で選んでいる台数。0台なら2台で見せる) + const kn = state.robots || 2, kx = rows.find(y => y.n === kn), ks = same.find(y => y.n === kn); + const of0 = state.robots === 0 ? "(0台を選んでいるので2台で)" : ""; + let cards; + if (!kx) cards = []; + else { + const c = kx.c; + const costH = COST.pay === "rent" + ? `1台 月${man(COST.price.rent)}+保守・充電 × ${kn}台` + : `初めに${man(c.first)}${c.subsidy ? `(補助金${man(c.subsidy)}を引いた額)` : ""}を${COST.years}年で割り、保守・充電を足した額`; + const labV = !base || !kx.m ? "—" : man(Math.max(kx.labor, 0)).replace(/^0万円$/, "0円"); + const labH = !base ? "ロボットなしで店が回らないので比べられない" + : !kx.m ? "選べる人数では店が回らない" + : kx.saved > 0 ? `店が回る最少のスタッフ ${base.st}人 → ${kx.m.st}人(${kx.saved}人分)` + : `店が回る最少のスタッフは${base.st}人のまま`; + const walk = ks && ks.r ? Math.round((1 - ks.r.walk_km_per_staff / r0.walk_km_per_staff) * 100) : null; + const netV = kx.net === null ? "—" : man(kx.net); + const netH = kx.net === null ? "" + : kx.saved > 0 && COST.pay === "buy" && kx.labor > c.run ? `初めに払う額は、およそ${Math.ceil(c.first / (kx.labor - c.run))}か月で取り戻せる` + : kx.saved > 0 ? "浮く人件費 − ロボットの費用" + : ks && ks.r ? `人数そのままなら、歩く距離 ${walk >= 0 ? "−" : "+"}${Math.abs(walk)}%・入店待ち ${plainMin(r0.entry_wait_mean)} → ${plainMin(ks.r.entry_wait_mean)}` : ""; + const yen = v => v.replace("万円", "万円").replace(/^0円$/, "0円"); + cards = [ + { k: `ロボット${kn}台の費用(月)${of0}`, v: yen(man(c.month)), h: costH }, + { k: "浮く人件費(月)", v: labV === "—" ? "—" : yen(labV), h: labH }, + { k: "差し引き(月)", v: netV === "—" ? "—" : yen(netV), h: netH }, + ]; + } + document.getElementById("cost-kpis").innerHTML = cards.map(x => `
${x.k}${x.v}${x.h}
`).join(""); + + const a = [ + ["人件費の差の出し方", `左の条件(店・混み具合・使い方・運び方)のまま、ロボット0〜${maxR}台それぞれで「店が回らない」が出ない最少のスタッフを、上の試算の結果から探す。ロボットなしより減った人数 × 浮く時間 × ピークの回数 × ${COST_DAYS}日 × 時給`], + ["浮く時間", `「ピークの間」は、この店のピーク${s.peak_min}分。「4時間」はシフト1回分。いまの設定では、1人あたり月${man(perPerson)}`], + ["時給", "社会保険料・交通費などの上乗せは入れていない。2026年度の最低賃金は全国加重平均で1,177円(9月3日に各都道府県の答申が出そろった。10月1日〜12月2日に順に発効)"], + ["ロボットの値段の相場", "買う場合は150〜350万円(中央値250万円前後)、レンタル・リースは月3〜12万円(中央値は6万円台)"], + ["保守・充電", `リースの月額に保守が含まれず、別に数万円かかる契約がある。保守の金額は仮置きで、込み・1万円・2万円から選ぶ。充電の電気代は1台月${COST_CHARGE.toLocaleString("ja-JP")}円で固定(目安は月1,000〜3,000円)`], + ["買う場合のひと月の費用", "(買った値段 − 補助金)÷ 使う月数 + 保守 + 充電。残った価値(売るときの値段)は入れていない"], + ["補助金(中小企業省力化投資補助金・カタログ注文型)", `カタログに載った製品を販売事業者と一緒に申請する。補助率は1/2以下。上限は従業員5人以下 500万円・6〜20人 750万円・21人以上 1,000万円(大幅な賃上げをすると上限が上がる。2026年3月19日の改定後)。この試算では買う場合にだけ1/2を引き、上限は750万円で計算する。随時受付で2027年3月末ごろまでの予定(予算がなくなると早く終わることがある)。申請には GビズIDプライム(取得に2〜3週間)が要る。配膳ロボットは対象の製品として扱われている。リースでの導入が対象になるかは、公募要領で確かめる`], + ["そのほかの支援", "業務改善助成金(厚生労働省):店の中で一番低い時給を50円以上上げ、設備投資などをすると費用の3/4〜4/5が出る。令和8年度の申請は、店のある都道府県の最低賃金が発効する前日まで。配膳ロボットが対象になるかは労働局で確かめる"], + ]; + const src = [ + ["配膳ロボットの導入費用の相場(お名前.com ビジネス)", "https://www.onamae.com/business/article/306061/"], + ["配膳ロボットのリース・保守・充電の費用(創業手帳)", "https://sogyotecho.jp/inshokuten-haizenrobot/"], + ["2026年度の地域別最低賃金(労働政策研究・研修機構)", "https://www.jil.go.jp/kokunai/blt/backnumber/2026/10/special_03.html"], + ["省力化投資補助金 カタログ注文型(補助金ポータル)", "https://hojyokin-portal.jp/columns/shoryokuka-catalog"], + ["省力化補助金の介護業での活用(厚生労働省・令和8年4月30日)", "https://www.mhlw.go.jp/content/001696868.pdf"], + ["令和8年度 業務改善助成金のご案内(厚生労働省)", "https://www.mhlw.go.jp/content/11200000/001693381.pdf"], + ]; + document.getElementById("cost-assume").innerHTML = a.map(([k, t]) => `
  • ${k}:${t}
  • `).join("") + + `
  • 出典:${src.map(([t, u]) => `${t}`).join("、")}
  • `; +} + @@ -673,0 +932 @@ buildControls(); +buildCost(); @@ -674,0 +934,8 @@ render(); +document.getElementById("cost-jump").addEventListener("click", e => { + const t = document.getElementById("cost"); + let f = null; try { f = window.frameElement; } catch (_) {} + if (!f) return; + e.preventDefault(); + const pw = window.parent; + pw.scrollTo({ top: f.getBoundingClientRect().top + pw.scrollY + t.getBoundingClientRect().top - 96, behavior: "smooth" }); +});