株式会社COTSUBU
kintone

kintoneで予約のダブルブッキングを防ぐ方法|期間の重なりを判定する考え方

宇田川 将也|10分
kintone予約管理ダブルブッキングプラグインJavaScript

kintoneの標準機能では、ダブルブッキングを防げません。「値の重複を禁止する」は同じ値かどうかを見る機能で、「期間が重なっているか」は判定できないためです。重なりを止めるには、JavaScriptを書くか、設定だけで済む無料プラグイン期間重複チェックを使います。

「期間が重なる」とはどういう条件か

ここを間違えると、動いているように見えて穴が開きます。既存の予約(9:00〜11:00)に対して、新しい予約がどこに入るかを全部並べます。

既存の予約   |----------|        9:00 ── 11:00
             9:00      11:00

A 前にある   |--|                 7:00 ──  8:00   重ならない
B 後ろに食い込む  |------|         10:00 ── 12:00   重なる
C すっぽり入る    |--|             9:30 ── 10:00   重なる
D またぐ    |---------------|      8:00 ── 13:00   重なる
E 後ろにある            |--|      12:00 ── 13:00   重ならない

この5つを1つの式で拾える条件が、次の1行です。

既存の開始 < 自分の終了 かつ 自分の開始 < 既存の終了

言い換えると「相手が自分の終わりより前に始まっていて、自分が相手の終わりより前に始まっている」です。BもCもDも、この条件に当てはまります。AとEは当てはまりません。

よくある判定ミス

現場で書かれがちな「惜しい条件」を並べます。

書きがちな条件 見逃すパターン
開始日時が既存の期間の中に入っているか D(既存をまるごとまたぐ長い予約)
終了日時が既存の期間の中に入っているか D、および A と同日の短い予約
開始日時が完全に一致するか B・C・D のすべて
同じ日に同じ会議室のレコードがあるか 重なっていない予約まで止めてしまう

とくにD を見逃すパターンが多いです。「終日おさえ」の予約が既存の短い予約と衝突しても気づけず、当日に鉢合わせします。

端(さかいめ)の扱いを先に決める

「10:00〜11:00 の次に 11:00〜12:00 は入れていいか」は、業務によって答えが違います。

対象 ふつうの答え 理由
会議室・設備(日時) 入れてよい 11:00に前の会議が終わり、次が始まる
社用車の貸出(日付) 入れてはいけない 「◯日〜◯日」はその日いっぱい使う
内見・面談(日時) 入れてよい(移動時間を別途考える) 枠が連続するのが普通

条件の不等号を < にするか <= にするかの違いです。日時なら「同時刻は重ならない」、日付なら「同じ日が含まれたら重なる」を既定にしておくと、たいていの業務に合います。

JavaScriptで書くとどうなるか

会議室の予約で、同じ会議室の期間が重なる予約を保存前に止める最小の例です。

(() => {
  'use strict';
  const TARGET = '会議室';     // ドロップダウンなど
  const START = '開始日時';    // 日時
  const END = '終了日時';      // 日時

  const esc = (v) => String(v).replace(/"/g, '\\"');

  kintone.events.on(
    ['app.record.create.submit', 'app.record.edit.submit'],
    async (event) => {
      const r = event.record;
      if (!r[TARGET].value || !r[START].value || !r[END].value) return event;

      if (r[START].value >= r[END].value) {
        r[END].error = '終了が開始より前になっています';
        return event;
      }

      // 重なる = 既存の開始 < 自分の終了 かつ 自分の開始 < 既存の終了
      const query =
        `${TARGET} = "${esc(r[TARGET].value)}"` +
        ` and ${START} < "${esc(r[END].value)}"` +
        ` and ${END} > "${esc(r[START].value)}" limit 10`;

      const resp = await kintone.api('/k/v1/records', 'GET', {
        app: kintone.app.getId(),
        query,
        fields: ['$id', START, END],
      });

      const selfId = String(kintone.app.record.getId());
      const hit = resp.records.filter((x) => x.$id.value !== selfId);
      if (hit.length > 0) {
        r[START].error =
          `同じ${TARGET}に重なる予定があります(レコード番号 ${hit[0].$id.value})`;
      }
      return event;
    }
  );
})();

40行ほどです。重なり判定をクエリの and 2つに落とし込んでいるのがポイントで、全レコードを取ってきて突き合わせる必要がありません。編集中は自分自身が引っかかるので、レコード番号で除いています。JavaScriptカスタマイズの基本は13章 JavaScript・API・外部連携にまとめています。

実運用で足りなくなるところ

足りないところ 何が必要になるか
キャンセル済みの予約まで止めてしまう ステータスの条件をクエリに追加
対象が「会議室+拠点」の組み合わせ 条件を組み立てる処理を可変にする
「止める」ではなく「確認して保存できる」にしたい 確認ダイアログと、無視して保存する経路を追加
一覧のインライン編集からの保存 app.record.index.edit.submit を別に指定
日付フィールドで端を含めたい 不等号を <= >= に変え、日付と日時で処理を分ける
通信に失敗したとき 保存を止めるべきではない(チェックのせいで業務が止まる)

最後は見落とされがちです。ネットワークが不安定なときにチェックが失敗して保存できなくなると、現場は「kintoneが壊れた」と受け取ります。

JavaScriptでもプラグインでも避けられない限界

どちらもブラウザ側の判定です。 2人がまったく同じ瞬間に予約を保存した場合、両方が「重なりなし」と判定してすり抜ける可能性があります。この点はkintoneで重複を禁止・チェックする方法で書いた重複チェックと同じで、期間の重なりはkintoneのサーバー側で保証する手段がありません。予約が秒単位で競合する規模になったら、kintoneの外に予約の受付口を作る設計を検討してください。

設定だけで済ませる:期間重複チェック(無料)

COTSUBUが公開している無料プラグイン期間重複チェックは、上の内容を設定画面だけで実現します。

  • 同じ対象(会議室・車両・担当者など)で、期間が重なる予定を保存前に検出
  • 日付(◯日〜◯日)と日時(◯時〜◯時)の両方に対応
  • 「終了と次の開始が同じ」を重なりとみなすかを選べる(日付は含む・日時は含まないが既定)
  • 対象は複数の項目の組み合わせにできる(例:会議室+拠点)。ユーザー選択にも対応
  • ルールごとに「保存を止める」か「確認して保存できる」かを選べる
  • 開始が終了より後になっている入力は、常に止める
  • 調べる途中で通信に失敗した場合は、保存を止めない

無料・会員登録不要・外部と通信しません。 kintoneの公式APIだけで動くので、自治体のLGWAN環境や、閉じた社内ネットワークでも使えます。

設定はこの4ステップです。

  1. アプリの設定 →「プラグイン」→「期間重複チェック」の歯車アイコン
  2. 「+ ルールを追加」で、対象と期間の開始・終了の項目を選ぶ。組み合わせたい場合は「+対象」で足す
  3. 端の扱いと、「保存を止める」か「確認して保存できる」かを選ぶ
  4. 「保存する」→「アプリを更新」

対象・開始・終了のどれかが空欄なら調べません。対象の項目を選ばなければ、アプリの中のすべてのレコードと比べます(社内に会議室が1つしかない場合など)。

対象に使えるのは、文字列(1行)・数値・リンク・ドロップダウン・ラジオボタン・ユーザー選択・組織選択・グループ選択です。

実務の例で考える

内見予約(不動産)

物件と担当者、どちらの重なりを防ぎたいかで設定が変わります。

防ぎたいこと 対象にする項目
同じ部屋に同じ時間帯の内見が2件 物件名(+部屋番号)
同じ担当者が2件の内見を掛け持ち 担当者(ユーザー選択)
同じ物件を別の営業所が押さえる 物件名+拠点

実務では担当者の重なりのほうが事故になります。部屋は空いていても、案内する人がいません。ルールを2つ作り、物件と担当者の両方をチェックするのが安全です。

面談・面接の予約

面接官のユーザー選択を対象にし、「保存を止める」ではなく「確認して保存できる」にしておくと、急ぎで押さえたいときに止まりません。重なりに気づいたうえで、あとから調整する運用に向きます。

社用車の貸し出し

貸出日〜返却日の日付で持ち、車両番号を対象にします。日付なので端を含める扱い(同じ日が含まれたら重なる)が既定です。「朝返して午後から別の人」という運用があるなら、日時フィールドに変えてください。

会議室

もっとも素直な使い方です。開始日時〜終了日時で、会議室を対象にします。10:00〜11:00 の次に 11:00〜12:00 は入れられます。

いずれも「1つの対象を、時間で奪い合う」構造です。この形の業務が複数ある場合は、予約アプリを業務ごとに分けるより、対象の種類(会議室/車両/人)をフィールドで持った1つの予約アプリにまとめたほうが運用が楽になることが多いです。判断の基準は5章 アプリ設計の基本にまとめています。

まとめ

  • 標準の「値の重複を禁止する」では期間の重なりを判定できない
  • 正しい条件は「既存の開始 < 自分の終了 かつ 自分の開始 < 既存の終了」の1行
  • 「開始が既存の期間に入っているか」だけでは、既存をまたぐ長い予約を見逃す
  • 端の扱い(終了と次の開始が同じ)は、日時なら重ならない、日付なら重なるが既定
  • JavaScriptなら40行ほど。通信失敗時に保存を止めない作りにすること
  • 設定だけで済ませるなら無料の期間重複チェック。担当者(ユーザー選択)も対象にできる
  • ブラウザ側の判定なので、完全に同時の保存はすり抜ける可能性がある

→ kintone導入支援の詳細 → kintone無料プラグイン一覧 → kintoneで重複を禁止・チェックする方法

よくある質問

kintoneの標準機能でダブルブッキングを防げますか

防げません。標準の「値の重複を禁止する」は1つのフィールドの値が同じかどうかを見る機能で、「期間が重なっているか」は判定できません。JavaScriptか、無料プラグインの期間重複チェックを使います。

「開始日時が既存の予約の中に入っているか」だけ見ればよいのでは

足りません。既存の予約をまるごとまたぐ長い予約(9:00〜18:00 の中に 10:00〜11:00 の既存予約がある状態)を見逃します。正しい条件は「既存の開始 < 自分の終了 かつ 自分の開始 < 既存の終了」です。この1行で5つのパターンすべてを拾えます。

10時〜11時の予約のあとに、11時〜12時の予約は入れられますか

日時の項目なら、期間重複チェックプラグインの既定では入れられます(終了と次の開始が同時刻なら重ならない扱い)。設定で「重なる」扱いにも変えられます。日付の項目の場合は、同じ日が含まれると重なる扱いが既定です。

担当者の予定の重なりもチェックできますか

できます。期間重複チェックプラグインは対象にユーザー選択フィールドを使えるため、「同じ担当者に同じ時間帯の予定が入る」ことを保存前に止められます。会議室+拠点のように複数項目の組み合わせを対象にすることもできます。

期間重複チェックプラグインは無料ですか

無料です。会員登録も不要で、MITライセンスのため商用でも使えます。外部のサーバーと通信しないので、自治体のLGWAN環境や閉じた社内ネットワークでも動きます。

宇田川 将也/ 代表取締役

2024年に総務省の地域活性化起業人として島根県海士町に着任。役場に住み込んで業務改善に従事し、kintoneで電子決裁や定期健診管理を構築しました。

経歴を見る

この記事について相談する

貴社・貴団体の状況に合わせて、最適な進め方をご提案します。

30分相談を予約する