フィットネス・健康

パーソナルトレーナーのシフトと予約枠を連動させる設計。指名予約、複数店舗の掛け持ち、休憩と移動時間を空き枠の計算に入れる

パーソナルジムの予約システムで、トレーナーのシフトと予約枠を連動させる設計を、開発する立場からまとめます。空き枠を計算で出す考え方、指名とおまかせの違い、複数店舗の移動時間、休憩の確保、二重予約の防ぎ方まで。

この記事の結論:パーソナルトレーナーのシフトと予約枠を連動させるには、空き枠を表として保存せず、「シフトから、入っている予約と、その前後の準備時間、休憩、店舗の間の移動時間を差し引いた残り」として、その都度計算する作りにします。指名予約は1人のトレーナーの時間だけを見て、おまかせは条件に合う全員の空きを重ねて見せます。複数の店舗を掛け持ちするトレーナーは、店舗ごとではなく1本の時間軸で予定を持ち、店舗が変わる前後に移動の時間を自動で入れます。最後に、ほぼ同時に届いた2件の予約がぶつからないよう、最後の砦としてデータベースの制約を置きます。

想定している読み手は、トレーナー制のジムで予約の仕組みを組む開発者と、その仕様を決める運営側の担当者です。キャンセルや振替の規定、シフトを変えるときの連絡の順番といった運用ルールの決め方は扱わず、シフトと空き枠をつなぐデータの持ち方と計算に絞ります。特定のお店の事例や、予約数の目安のような数字は出てきません。

シフトと予約枠を連動させるには、空き枠を保存せずに計算する

トレーナーのシフトと予約枠を連動させるいちばん確実な方法は、空き枠そのものをデータとして持たないことです。元になるデータだけを保存し、会員が予約の画面を開いたときに、そこから空き枠を求めます。

空き枠を表として作り置きすると、シフトを変えたとき、休憩をずらしたとき、トレーナーが別の店舗に入ったときのそれぞれで、作り置いた表を直す処理が必要になります。どれか一つでも直し損ねると、いないはずのトレーナーの枠が予約できてしまいます。

元にするデータは、次の5つです。

データ 中身 変える人
トレーナー 名前、担当できるメニュー、勤務できる店舗 運営の管理者
勤務の予定(シフト) 日付、店舗、開始と終了の時刻 管理者、またはトレーナー本人の申告
予約 会員、メニュー、トレーナー、店舗、開始と終了 会員、受付スタッフ
ふさがっている時間 休憩、研修、清掃、移動など、予約を入れない時間 管理者、または自動で作る
メニュー 所要時間、前後に必要な準備の時間 運営の管理者

この形にしておくと、シフトを1か所直すだけで、その日の空き枠が自動で正しくなります。データの正本が1つずつしかないので、表どうしの食い違いが起きません。

空き枠は「シフト−予約−準備−休憩−移動」の残りから求める

空き枠の計算は、トレーナーごとに、勤務の時間帯から使えない時間を順に引いていき、残った連続した時間にメニューが収まるかを確かめる、という手順で行います。

  1. 勤務の時間帯を取り出す。 その日、その店舗にいるシフトの開始から終了まで
  2. 入っている予約を引く。 予約の時間だけでなく、メニューに決めた前後の準備の時間も含めて引く
  3. ふさがっている時間を引く。 休憩、研修、清掃などの時間
  4. 移動の時間を引く。 同じ日に別の店舗の予定がある場合、その前後に移動の時間を引く
  5. 残りの時間に、選ばれたメニューが収まる開始時刻を並べる。 メニューの所要時間と前後の準備を足した長さが入るかで判定する
  6. 開始時刻の刻みをそろえる。 30分ごとなど、店で決めた刻みに合う時刻だけを出す

ポイントは、準備の時間を「予約の外側の余白」として、メニューごとに持つことです。前の会員の片付けや器具の消毒、次の会員の記録を読む時間はメニューによって違うので、予約の長さそのものに含めず別に持つと、会員に見せる時間(たとえば60分のトレーニング)と、トレーナーの手がふさがる時間(前後を足した時間)を分けて扱えます。

開始時刻の刻みを細かくしすぎると、予約の間に半端な隙間が残り、その隙間に入るメニューがなくて使われない時間が増えます。刻みは、いちばん多いメニューの長さに合わせて決めるのが扱いやすいところです。

トレーナー指名の予約は、指名ありとおまかせで空き枠の出し方を変える

トレーナーの指名を受け付けるなら、空き枠の出し方を「指名あり」と「おまかせ」で分けます。指名ありはその1人の空きだけを、おまかせは条件に合うトレーナー全員の空きを重ねて見せます。

予約の種類 空き枠の出し方 予約を確定するとき
指名あり 指名されたトレーナーの空きだけを計算する そのトレーナーで確定する
おまかせ メニューを担当できる全員の空きを計算し、誰か1人でも空いていれば枠として出す 確定の瞬間に、空いている人の中から割り当てる
前回と同じ担当を希望 前回の担当者を指名ありとして扱い、空きがなければおまかせの枠も案内する 会員が選んだ方で確定する

おまかせの割り当ては、確定の瞬間に行うのがコツです。画面を表示した時点で担当を決めてしまうと、会員が迷っている間にその人の枠が別の予約で埋まり、確定の段階で失敗します。

割り当てのルールも決めておきます。予約の少ない人から順に割り当てる、指名の多い人を後回しにして指名用に時間を残す、など、店の方針によって変わる部分です。ルールは1つの関数にまとめておくと、方針を変えたいときにそこだけを直せます。

指名に追加の料金をもらう店では、予約のデータに「指名だったか」を残しておきます。あとでトレーナーを差し替える必要が出たとき、指名の予約かどうかで会員への連絡の仕方が変わるからです。

複数店舗を掛け持ちするトレーナーは、1本の時間軸で予定を持つ

複数の店舗を掛け持ちするトレーナーの予定は、店舗ごとに分けず、トレーナー1人につき1本の時間軸で持ちます。店舗ごとに予定を分けると、別々の店舗で同じ時間に予約が入る、という重なりを見落とします。

1本の時間軸で持ったうえで、次の3つを仕組みに入れます。

  • 店舗の間の移動時間を表にする。 「店舗A→店舗B」の移動にかかる時間を、店舗の組み合わせごとに持つ。行きと帰りで違う場合も分けて持てるようにする
  • 店舗が変わる前後に、移動の時間を自動で置く。 同じ日にA店のシフトのあとにB店のシフトがある場合、A店の最後の予約の終わりから、B店の最初の予約の始まりまでに、移動時間以上の空きがあるかを確かめる
  • シフト自体の重なりを登録の時点で止める。 同じ時間帯に2つの店舗のシフトを入れようとしたら、保存させない

移動の時間を、ふさがっている時間のデータとして実際に作るか、空き枠の計算の中だけで考えるかは、作り方の好みです。管理画面でトレーナーの1日を見たときに「移動」と表示したいなら、データとして作っておくと、見た目と計算が一致します。

会員の画面では、店舗を先に選んでもらい、その店舗にいる時間のトレーナーだけを出します。掛け持ちのトレーナーを指名した会員には、そのトレーナーがどの曜日にどの店舗にいるかを分かるようにしておくと、店舗の選び直しが減ります。

休憩は、予約で埋まる前に時間を確保しておく

休憩は、予約の空いたところに後から入れるのではなく、シフトを作るときに、ふさがっている時間として先に置いておきます。後から入れようとすると、人気のトレーナーほど休憩を入れる隙間がなくなります。

雇用しているトレーナーの場合、休憩は法律で決まった最低限があります。厚生労働省の案内では、労働時間が6時間を超える場合は少なくとも45分、8時間を超える場合は少なくとも1時間の休憩を、労働時間の途中に与えることになっています。シフトを保存するときに、勤務の長さに応じた休憩が入っているかを確かめ、足りなければ警告を出すと、作り忘れを防げます。

確かめること 仕組みでの確かめ方
勤務の長さに応じた休憩があるか シフトの長さと、休憩の合計を比べる
休憩が勤務の途中にあるか 休憩が始業の直後や終業の直前だけになっていないかを見る
休憩の時間に予約が入っていないか 予約の確定時に、ふさがっている時間と重ならないかを確かめる
休憩を動かしたとき 動かした先に予約がないかを確かめてから保存する

業務委託のトレーナーは、扱いが変わります。業務委託の人に対して、働く時間を事業者が一方的に決めて拘束するような運用は、雇用に近い働き方と見られるおそれがあります。厚生労働省のフリーランス向けガイドラインの解説でも、指揮監督の有無や、時間・場所の拘束が、労働者に当たるかどうかの判断の要素として挙げられています。

そのため、業務委託のトレーナーの予定は、システムの中で「割り当てたシフト」ではなく、「トレーナー本人が申告した受付可能な時間」として持つ作りにしておくと、契約の形と画面の言葉がそろいます。データの形は同じでも、誰が入力するか、管理者が変えられるかを分けておきます。契約の形そのものは、社会保険労務士や弁護士に相談して決めます。

同時に押された予約が重ならないよう、データベースで止める

二重予約を確実に防ぐには、画面の確認だけに頼らず、データベースに「同じトレーナーの予約の時間帯は重ならない」という制約をかけます。空きを表示してから保存が終わるまでのわずかな間に、別の会員が同じ枠を確定すると、確認だけでは両方とも通ってしまうからです。

PostgreSQL なら、時間の範囲を表す型と排他制約で、この条件を表せます。公式ドキュメントにも、範囲が重なる行を拒む排他制約の例と、btree_gist 拡張を使って「同じ部屋の場合だけ」重なりを拒む例が載っています。トレーナーの予約の表に置き換えた例が下のコードです。

sql
CREATE EXTENSION IF NOT EXISTS btree_gist;

-- 予約。手がふさがる時間(前後の準備を含む)を busy に入れる
CREATE TABLE booking (
  id          bigserial PRIMARY KEY,
  trainer_id  bigint      NOT NULL,
  store_id    bigint      NOT NULL,
  member_id   bigint      NOT NULL,
  menu_id     bigint      NOT NULL,
  starts_at   timestamptz NOT NULL,  -- 会員に見せる開始
  ends_at     timestamptz NOT NULL,  -- 会員に見せる終了
  busy        tstzrange   NOT NULL,  -- 準備を含めた、ふさがる時間
  status      text        NOT NULL DEFAULT 'confirmed',
  -- 取り消した予約は重なりの判定から外す
  EXCLUDE USING gist (trainer_id WITH =, busy WITH &&)
    WHERE (status = 'confirmed')
);

この制約があれば、同じトレーナーで手がふさがる時間が重なる予約は、保存の段階でエラーになります。アプリの側では、このエラーを受け取ったら「この時間は直前に埋まりました」と表示し、近い空き枠を案内します。

範囲の型は、既定では開始を含み終了を含まない形([開始, 終了))になるので、10時ちょうどに終わる予約と10時ちょうどに始まる予約は重なりとみなされません。前後の準備の時間を busy に含めておけば、準備の時間まで含めて重なりを止められます。休憩や移動のような、ふさがっている時間も同じ表に入れるか、同じ制約を持つ別の表に入れておくと、予約との重なりも同じ仕組みで止められます。

シフトを変えるときは、入っている予約との食い違いを保存の前に見せる

シフトを短くしたり、店舗を変えたりするときは、保存する前に、その変更で勤務時間の外にはみ出す予約がないかを調べて一覧で見せます。はみ出す予約があるまま保存できる作りにすると、シフトの上では休みなのに予約が残っている、という状態が生まれます。

仕組みとしては、変更後のシフトの時間帯に収まらない予約を検索し、1件でもあれば保存を止めて、担当の差し替えか会員への連絡を先に済ませるよう促します。指名の予約か、おまかせの予約かで次の動きが変わるので、一覧にはその区別も出します。

シフトを変えるときに、誰に、どの順で連絡するかといった運用の決め方は、ジムの予約の運用ルールをまとめた記事に書いています。

作り始める前に、この表の空欄を埋めておく

ここまでの内容を、作り始める前に決めておく項目として並べます。この表に空欄が残っていると、作っている途中で仕様が変わり、空き枠の計算をやり直すことになります。

項目 決めること
開始時刻の刻み 何分ごとの開始を出すか
メニューごとの準備の時間 前と後にそれぞれ何分か
指名の受け方 指名あり・おまかせ・前回と同じ担当、のどれを出すか
おまかせの割り当て 誰から順に割り当てるか
店舗の間の移動時間 店舗の組み合わせごとの時間
休憩 勤務の長さごとの最低限と、置く時間帯
業務委託の扱い 受付可能な時間を誰が入力し、誰が変えられるか
二重予約の防ぎ方 データベースの制約で止めるか、ほかの方法をとるか
シフト変更の検証 はみ出す予約があるときに保存を止めるか

株式会社bundlyzeは、トレーナー制に限らずさまざまな業種の店で使える予約管理システムについて、企画と開発に加え、稼働したあとの運用も自社で受け持っています。トレーナー制の予約を店の決まりに合わせて組み立てたいときは、予約や会員管理を含む業務システム開発の案内をご覧ください。

出典

次の3つの資料は、2026年10月3日の時点の内容で書いています。休憩の決まりやデータベースの仕様は変わりうるため、実装の前にリンク先で現行の内容を見直してください。

よくある質問

空き枠は、あらかじめ予約枠の表として作っておくほうが簡単ではありませんか?

最初は簡単ですが、シフトの変更や休憩の移動のたびに、作っておいた枠の表を直す作業が発生し、直し忘れると「シフトにいないトレーナーの枠が開いている」状態が起きます。シフト・予約・休憩などの元になるデータだけを持ち、空き枠は画面を開いたときに計算して出す作りにすると、元のデータを直せば空き枠も自動で正しくなります。

業務委託のトレーナーにも、システムでシフトを割り当てていいですか?

業務委託の場合、働く時間を事業者が一方的に決めて拘束するような運用は、雇用に近い働き方と見られるおそれがあります。厚生労働省などのガイドラインでは、指揮監督の有無や時間的・場所的な拘束が労働者かどうかの判断要素に挙げられています。システム上は「割り当てたシフト」ではなく「トレーナーが申告した受付可能な時間」として持ち、契約の形は専門家に確認してください。

同じ時間に2人の会員が同じトレーナーを予約しようとしたら、どうなりますか?

画面で空いているかを確かめるだけでは、ほぼ同時に送られた2件の予約を両方受け付けてしまうことがあります。データベースの側で「同じトレーナーの予約の時間帯は重ならない」という制約をかけておけば、後から届いた1件は保存の段階で弾かれます。弾かれた会員には、別の空き枠を案内する画面を出します。