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つしかない場合など)。
対象に使えるのは、文字列(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環境や閉じた社内ネットワークでも動きます。


