予約システムを作ろうとして最初に悩んだのは、「予約枠をスプレッドシートでどう表現するか」だった。
これは言い換えると「在庫をどう表現するか」という問いだ。飲食店の席数、ホテルの客室、コンサートのチケット。予約可能な「枠」は全て在庫だ。あればOK、なければNG。二重予約(在庫のマイナス)は絶対に起こしてはいけない。
カレンダー型かリスト型か
スプレッドシートで予約枠を管理するとき、設計は2つに分かれる。
カレンダー型は日付を行に、時刻を列に並べて、セルに「〇」か「×」を入れる。見た目は分かりやすい。しかし、サービスが複数になると途端に複雑になる。「片づけメソッドの片づけレッスン(5時間)」と「暮らしの作戦会議(60分)」が混在するとき、同じカレンダー上に並べるのか、シートを分けるのか。
リスト型は1行が1つの枠を表す。service_id・日付・時刻・予約済みフラグが列に並ぶだけだ。見た目はデータベースのテーブルに近い。GASからの操作がシンプルになる。「このservice_idの空き枠を全件取得する」というクエリがフィルタリングで書ける。
リスト型を選んだ。操作する側(GAS)の都合で設計した。
列の意味を決める
slotsシートの列は以下になった。
slot_id、service_id(1〜6)、日付、時刻、予約済みフラグ(空白=空き / "booked"=予約済み)、予約者名、電話番号、メールアドレス、メモ、予約ID、LINEユーザーID、予約日時。
slot_idが最初にある理由がある。「何行目を予約済みにするか」を判断するとき、行番号を使ってはいけない。行の挿入や削除でズレる。slot_idを持ち歩いて、IDで照合してから書き込む。
予約済みフラグが「true/false」ではなく「空白/"booked"」なのは、スプレッドシートを人間が見たとき、空白の方が「空き」として直感的だからだ。機械より人間の読みやすさを優先した。
空き枠を取得するコード
フロントエンドから「service_idが2の空き枠を教えて」というリクエストが来ると、GASはこれを実行する。
function getSlots(serviceId) {
const ss = SpreadsheetApp.openById(SHEET_ID);
const sheet = ss.getSheetByName('slots');
const rows = sheet.getDataRange().getValues();
const today = new Date();
today.setHours(0,0,0,0);
return rows.slice(1).filter(r =>
r[1] == serviceId && r[4] === '' && new Date(r[2]) >= today
).slice(0,20).map(r => ({
slot_id: r[0], date: r[2], time: r[3],
label: r[2] + ' ' + r[3]
}));
}
全データを取得してフィルタリングする。スプレッドシートをDBとして使う場合の常套手段だ。枠の数が数百件以下なら性能上の問題はない。
ダブルブッキングを防ぐ
空き枠を取得してから予約確定するまでの間に、別の人が同じ枠を選んでいることがある。
「確認したら空き→予約確定を書き込む」の間にロックを入れない設計では、2人が同時に押したとき両方に「予約できました」と返してしまう可能性がある。
GASのLockServiceを使う。
const lock = LockService.getScriptLock();
lock.waitLock(5000);
try {
const row = findSlotRow(slotId);
if (row[4] !== '') {
return { success: false, message: 'すでに予約が入りました' };
}
writeBooking(row, bookingData);
return { success: true };
} finally {
lock.releaseLock();
}
ロック取得後に枠の状態を再確認する。「ロックを取る前に空きだった」は信頼できない。ロックを取った後で初めて確認する。
管理が「Googleスプレッドシートを開くだけ」で済む理由
オーナーがスマートフォンからスプレッドシートアプリを開いて、新しい行を追加する。service_id・日付・時刻を入力して、フラグの列を空白にしておく。それだけで新しい予約枠として公開される。
管理画面にログインする必要はない。専用ツールを覚える必要もない。スプレッドシートを使えれば、それがシステムの操作方法になる。
「伊藤がいなくても使い続けられるか」を設計の基準にした。答えがYesだと確信できた設計だった。
*次回は「LINE通知設計:予約確認・前日リマインド・キャンセル通知の3本柱」*
*シンプルシステム株式会社 代表 伊藤勝彦*