シフト提出をLINEで自動集約する設計図
希望の回収から未提出者へのリマインド、確定シフトの本人宛て配信まで。スプレッドシート連携つきの実装ブループリント。
【Before】毎月20日前後、女将のスマホには個人LINEの希望休がバラバラに届きます。「15日休みたいです」「来月は火曜だけ入れません」。既読にしたまま忘れ、あとで探すとトーク履歴の奥に沈んでいる。締切を過ぎても3人分が届かず、一人ずつ電話をかけ直す。結局シフト表が出来上がるのは月末ぎりぎりで、スタッフからは「来月の予定が立てられない」と言われます。
【After】毎月1日、スタッフ全員のLINEに受付開始の案内が自動で届きます。スタッフはリッチメニューから希望を入力して送信するだけ。17日朝には未提出の人にだけリマインドが飛びます。21日朝、女将のメールに「提出 12 / 14名 未提出:〇〇、△△」と集計が届く。シフトを組み終えたら、25日に各自の勤務だけを書いた文面が用意され、送るだけになります。探す作業も、催促の電話も、転記もなくなります。
この記事では、この仕組みを架空の小さな旅館「川辺屋」(客室8室・スタッフ14名)を例に、コピペで動くところまで組み立てます。
1. この仕組みでできること・全体の流れ
この仕組みは3つの部品でできています。役割をはっきり分けるのが設計の要です。
| 部品 | 担当すること |
|---|---|
| エルメ | スタッフとのやりとりのすべて。フォームで受信し、予約配信で配布・リマインドし、1:1チャットで確定シフトを送る。タグで「スタッフか/提出済みか」を管理する |
| スプレッドシート | データの保存と集計。エルメが書く受け口1枚と、人が使う台帳4枚 |
| GAS | 受け口から台帳への取込と、管理者へのメール(提出状況の集計、確定シフトの本人宛て文面)。LINEには一切関わらない |
◆ 最初に押さえる技術的な制約
LINE公式アカウントの Webhook(メッセージを受け取る先)の設定は1つしか持てません。エルメを接続した時点で、その枠はエルメが使います。つまり GASに「受信」をさせることはできません。
そしてもう1つ。スタッフ個人にLINEを送るには相手のユーザーIDが要りますが、これはWebhookで受信したときにしか手に入りません。エルメの友だち詳細から写す方法もありますが、1人ずつ33文字を手で写す運用は、1文字の誤りでその人だけ届かなくなり、原因も分かりません。この記事では、スタッフへの送信もすべてエルメに寄せ、GASは「記録と管理者へのメール」だけに徹します。この形なら両者は問題なく共存し、LINEの認証情報を一切扱わずに済みます。
運用カレンダー(毎月の流れ)
| 日付 | 起こること | 誰が/何が |
|---|---|---|
| 前月末 | 前月の「シフト_提出済」タグを全員から一括解除。フォームの対象月の選択肢を翌月に更新 | 管理者(手作業・2分) |
| 1日 10:00 | 翌月分の受付開始を「スタッフ_在籍」の全員に案内 | エルメ(予約配信・自動) |
| 1日〜20日 | スタッフがフォームから希望を提出。回答は受け口に自動記録され、5分以内に台帳へ写される | スタッフ/エルメ/GAS |
| 17日 8:00 | 途中経過(提出○/未提出者名)が管理者にメールで届く | GAS(自動) |
| 17日 10:00 | 未提出の人にだけリマインドが届く | エルメ(予約配信・自動) |
| 20日 23:59 | 提出の締切 | — |
| 21日 9:00 | 最終集計が管理者にメールで届く | GAS(自動) |
| 21日〜24日 | シフトを組み、確定シフトシートに入力 | 管理者(手作業) |
| 25日 9:00 | 各自の勤務だけを書いた文面がシートとメールに用意される | GAS(自動) |
| 25日 勤務時間内 | 文面を1:1チャットで各自に送る(14名で10分ほど) | 管理者(手作業) |
| 翌月1日 | 勤務開始 | — |
リマインドは締切の3日前、確定シフトの配信は勤務開始の7日前。この間隔にしておくと、提出漏れにも差し替えにも余裕をもって対応できます。配布もリマインドも勤務時間内の時刻に予約するのは、章9で触れる労務上の理由からです。
2. スプレッドシートを用意する(列の設計)
先にここを固めます。列の名前と順番を最初に確定させないと、後でGASのコードと本文の説明がずれて必ず壊れます。 1つのスプレッドシートの中に、人が使う台帳4枚を作ります。受け口はあとで章4のフォーム連携のときにエルメが自動で作ります。
受け口「シフト希望提出」(エルメが作る・人は編集しない)
章4でフォームをスプレッドシート連携すると、このファイルの中に回答がリアルタイムで記録されるシートができます。A列が回答日時、B列が友だち名(LINEの表示名)、C列以降がフォームの質問の順です。GASは列の位置ではなく1行目の見出し名で読むので、質問を足しても動きます。ここに人が書き込むと壊れるので、振替や備考のメモは必ず台帳側に書いてください。
台帳①「スタッフ名簿」
| 列 | 列名 | 入れるもの |
|---|---|---|
| A | 職員番号 | 001、002 …(文字列書式にしておく) |
| B | 氏名 | 本名。LINEの表示名ではない |
| C | 部署 | フロント/客室/調理 など |
| D | 在籍状況 | 在籍 または 退職 |
D列は「在籍」という文字と完全に一致させてください。GASがこの文字を見て、集計の母数に入れるかどうかを判定します。LINEのユーザーIDを持つ列はありません。この記事ではGASからLINEへ送らないので、必要ないからです。
台帳②「シフト希望台帳」(GASが受け口から写す)
| 列 | 列名 | 入れるもの |
|---|---|---|
| A | 受付日時 | 受け口の回答日時をGASが写す |
| B | 友だち名 | LINEの表示名。エルメで本人を探すときの目印 |
| C | 職員番号 | フォームの回答。名簿のA列と照合する |
| D | 氏名 | フォームの回答(確認用) |
| E | 対象月 | 2026年10月 の形式。フォームの選択肢がそのまま入る |
| F | 希望休 | 5,12,19 のように日付をカンマ区切りで |
| G | 勤務可能な時間帯 | 終日/午前のみ/夕方以降 など |
| H | 備考 | 自由記入 |
| I | 取込キー | 回答日時+友だち名。GASが二重取込を防ぐために使う |
台帳③「確定シフト」(管理者が入力)
| 列 | 列名 | 入れるもの |
|---|---|---|
| A | 日付 | 2026/10/05 の形式(日付型のまま) |
| B | 職員番号 | 名簿のA列と一致させる |
| C | 氏名 | 確認用 |
| D | 勤務区分 | 早番/遅番/通し/休 など |
| E | 開始時刻 | 9:00(文字列書式で入力) |
| F | 終了時刻 | 17:30(文字列書式で入力) |
| G | 配信状況 | 空欄のまま。GASが文面を作ると「文面作成済」と書き込む |
台帳④「配信文」(GASが作る)
| 列 | 列名 | 入れるもの |
|---|---|---|
| A | 職員番号 | GASが書く |
| B | 氏名 | GASが書く |
| C | 本文 | その人の勤務だけをまとめた文面。コピーして1:1チャットに貼る |
シート名だけ作っておけば、中身は25日にGASが毎回書き直します。
つまずきポイント:E列・F列の書式
時刻をそのまま入力すると、スプレッドシートが勝手に「時刻型」に変換します。そのままGASで読むと Sat Dec 30 1899... のような表示になってしまいます。確定シフトのE列とF列は、あらかじめ 表示形式 > 数字 > 書式なしテキスト に設定しておいてください。
つまずきポイント:台帳に列を後から挿入しない
台帳はGASが列を「左から何番目か」で見ています。あとからA列の前に1列足すと、すべてのコードがずれて動かなくなります。項目を増やしたいときは、必ずいちばん右に追加してください。受け口のほうは見出し名で読むので、フォームに質問を足しても影響しません。
3. スタッフだけに「スタッフ_在籍」タグを付ける
この章が、仕組み全体の土台になります。旅館のLINE公式アカウントには宿泊のお客様も友だちとして入っています。配信対象を「全員」にすると、お客様にシフト希望の催促が届きます。スタッフとお客様を分ける柵を、最初に作ります。
なぜLINEの表示名ではダメなのか
表示名は本人がいつでも変えられます。ニックネームや絵文字だけの人もいます。同じ苗字のスタッフが2人いれば区別がつきません。そこで「スタッフかどうか」はタグで、「誰か」はフォームに書いてもらう職員番号で管理します。名簿(章2)の職員番号と本名が、両者をつなぐ鍵になります。
設定の手順
操作場所:エルメ管理画面 > タグ管理 と エルメ管理画面 > URLクリックアクション
- タグ「スタッフ_在籍」と「シフト_提出済」を作る
- 「URLクリックアクション」で、タップした人に「スタッフ_在籍」を付けるスタッフ登録用リンクを作る(リンク先は旅館のホームページでよい)
- スタッフ全員に、旅館のLINE公式アカウントを友だち追加してもらう
- スタッフにだけ、登録用リンクを個別に送る(朝礼で口頭+チャットで1人ずつ。お客様の目に触れる場所には貼らない)
- スプレッドシートの「スタッフ名簿」に、A〜D列を手入力する
- エルメの友だちリストで「スタッフ_在籍」で絞り込み、人数が名簿の在籍人数と一致することを確認する
14人程度なら、この作業は30分ほどで終わります。1回やれば以降は入退社のときだけの作業です。
つまずきポイント:途中入社と退職
入社したら「登録用リンクのタップ」と「名簿への追加」の2つが要ります。片方だけだと、配信は届くのに集計に出ない(またはその逆)という状態になります。入社手続きのチェックリストに両方を入れてください。退職者は「スタッフ_在籍」タグを外し、名簿のD列を「退職」に変えます。以降の配布・リマインドから自動的に外れます。名簿の行は消さないでください。過去のシフト記録との紐づけが切れます。
4. シフト希望フォームを作り、毎月1日に配布する(エルメ)
受信はすべてエルメが担当します。ここで作るフォームが、スタッフから見た唯一の入口になります。
フォームの設定手順
操作場所:エルメ管理画面 > フォーム作成 > 新規作成
- フォーム名を「シフト希望提出」にする
- 下の表のとおり質問を作る。質問名はそのままの文字で作る(受け口の見出しになり、GASがこの文字で列を探す)
- 「対象月」は自由入力にせず、選択式(プルダウン)にする。選択肢は翌月だけ(例:2026年10月)
- 「メッセージ・アクション設定」で、回答後のアクションに「タグを付ける:シフト_提出済」を設定する
- 回答後のお礼は完了画面に表示する文章で設定する。LINEのトークへの自動返信は付けない(付けると1回答につき1通、配信数を消費する。章8)
- フォーム作成一覧の「スプレッドシート」からGoogleアカウントを連携し、章2のスプレッドシートを指定する。できた受け口のシート名を章6のGASの
SHEET_LINKEDに写す
| 質問名 | 形式 | 補足 |
|---|---|---|
| 職員番号 | 自由入力(必須) | 「001」のように3桁で。名簿のA列と照合する |
| 氏名 | 自由入力(必須) | 確認用。「給与明細と同じ表記で」と添える |
| 対象月 | 選択(必須) | 「2026年10月」の形。ゼロ埋めしない(10月はよいが「09月」にしない) |
| 希望休 | 自由入力 | 例:5,12,19 |
| 勤務可能な時間帯 | 選択 | 終日/午前のみ/夕方以降 など |
| 備考 | 自由入力(任意) | 説明文に「理由の記入は不要です」と明記する(章9) |
つまずきポイント:対象月の表記ゆれ
ここが最大の落とし穴です。「2026年10月」「2026/10」「10月」が混在すると、GASが提出済みかどうかを判定できません。必ず選択式にして、「2026年10月」の形式に固定してください。GAS側もこの形式で照合します。選択肢は毎月末に翌月分へ書き換えます。
タグの設計
この仕組みで使うタグは2つだけです。増やすと管理しきれなくなります。
| タグ名 | 付けるタイミング | 外すタイミング |
|---|---|---|
| スタッフ_在籍 | 登録用リンクをタップしたとき(章3) | 退職時に手動で外す |
| シフト_提出済 | フォームに回答したとき(自動) | 毎月末、受付開始前に一括解除 |
解除の操作場所:エルメ管理画面 > 友だちリスト でタグ「シフト_提出済」で絞り込み → 全員を選択 → 一括でタグを外す。外し忘れると、先月出した人が今月の未提出リマインドから漏れます。
毎月1日の配布(予約配信で自動化)
操作場所:エルメ管理画面 > メッセージ配信 > 新規作成
- 管理用タイトルを「シフト希望フォーム配布」にする
- 本文に下のテンプレと、フォームの配信用URLを入れる
- 配信対象の絞り込みで、タグ「スタッフ_在籍」が付いている人にする。配信予定の人数がスタッフの人数と一致することを確認する
- 「配信予約」で毎月1日の勤務時間内(例:10:00)を指定して登録する
いつでも出せる入口(リッチメニュー)
操作場所:エルメ管理画面 > リッチメニュー
スタッフ向けのリッチメニューに「シフト希望」ボタンを作り、タップ先にフォームの配信用URLを設定します。表示条件を「スタッフ_在籍 が付いている人」にすれば、お客様のメニューには出ません。LINE公式アカウント管理画面側のリッチメニューはオフにしておきます(両方有効だと取り合いになります)。
この設計は、無料のまま組めます
この仕組みで使うエルメの機能は、フォーム1つ・タグ2つ・予約配信2本・リッチメニュー1つです。フリープランで作れる数の上限(フォーム数・リッチメニュー数など)は変わることがあるので、公式サイトで確認したうえで、まず実際の管理画面でフォームを1つ作ってみるところから始めてみてください。
5. 未提出者にだけリマインドする(エルメの予約配信)
全員に催促を送ると、すでに出した人は「また来た」と感じて、だんだん読まなくなります。出していない人にだけ届くのが、この仕組みの肝です。
章4で、フォームに回答した人には「シフト_提出済」が自動で付くようにしました。つまり「スタッフ_在籍」があって「シフト_提出済」がない人=まだ出していないスタッフです。この条件で予約配信を1本作れば、未提出者への催促は自動化できます。GASは要りません。
リマインドの予約配信を作る
操作場所:エルメ管理画面 > メッセージ配信 > 新規作成
- 管理用タイトルを「シフト未提出リマインド(17日)」にする
- 本文に下のテンプレとフォームの配信用URLを入れる
- 配信対象の絞り込みで、「スタッフ_在籍」が付いていて、「シフト_提出済」が付いていない人を条件にする
- 「配信予約」で毎月17日の勤務時間内(例:10:00)を指定して登録する
配信対象は送信の瞬間に決まります
予約を登録した時点ではなく、17日10:00の送信時点で条件に合う人に届きます。16日に出した人には届きません。逆に、前月の「シフト_提出済」を外し忘れていると、先月出した人が「提出済み」と見なされて催促から漏れます。章4のタグ解除が毎月の要です。
つまずきポイント:お客様に届いた
絞り込みに「スタッフ_在籍 あり」を入れ忘れると、「シフト_提出済がない人」=友だち全員(お客様を含む)が対象になります。配信を登録する画面で配信予定の人数を必ず見て、スタッフの人数以下になっていることを確認してください。
6. 回答を台帳に写し、締切後に集計する(GAS)
ここから先はGAS(Google Apps Script)を使います。やることは2つ。受け口の新しい回答を台帳に写すこと(5分おき)と、提出状況を管理者にメールすること(17日と21日の朝)です。LINEには一切関わらないので、トークンもユーザーIDも要りません。
準備
- 章2のスプレッドシートで 拡張機能 > Apps Script を開く
- 最初から書かれている
function myFunction() {}を消し、下のコードをまるごと貼る - 冒頭の
ADMIN_MAILを管理者のメールアドレスに、SHEET_LINKEDを章4でエルメが作った受け口のシート名に書き換える - 左の歯車(プロジェクトの設定)で、タイムゾーンが「Asia/Tokyo」であることを確認する
- 保存して、
importRequestsを1回手動で実行し、権限の確認画面で許可する
コード全文(このまま貼ってOK)
// ===== シフト希望集約GAS(時間トリガー専用・LINEには受信も送信もしない) =====
// 受け口(エルメが書く)→ シフト希望台帳(人が使う)へ新しい回答を写し、
// 提出状況の集計と、確定シフトの本人宛て文面を管理者へメールする。
// LINEのWebhookはエルメが占有したままなので、このGASはLINEに関わらない。
// ▼ ここだけ自分のものに書き換える -----------------------------
const ADMIN_MAIL = 'owner@example.com'; // 管理者(女将)の通知先メールアドレス
const SHEET_LINKED = 'シフト希望提出'; // エルメが作った受け口のシート名に合わせる
// -----------------------------------------------------------------
const SHEET_ROSTER = 'スタッフ名簿'; // A=職員番号 B=氏名 C=部署 D=在籍状況
const SHEET_REQ = 'シフト希望台帳'; // A=受付日時 B=友だち名 C=職員番号 D=氏名 E=対象月 F=希望休 G=時間帯 H=備考 I=取込キー
const SHEET_SHIFT = '確定シフト'; // A=日付 B=職員番号 C=氏名 D=勤務区分 E=開始 F=終了 G=配信状況
const SHEET_MSG = '配信文'; // A=職員番号 B=氏名 C=本文(GASが作る)
const TZ = 'Asia/Tokyo';
// 受け口の見出し名(フォームの質問名と一字一句同じにする)
const H = { AT: '回答日時', FNAME: '友だち名', NO: '職員番号', NAME: '氏名',
MONTH: '対象月', OFF: '希望休', TIME: '勤務可能な時間帯', NOTE: '備考' };
// ───── 共通 ─────
function ss_() { return SpreadsheetApp.getActiveSpreadsheet(); }
function mail_(subject, body) { MailApp.sendEmail(ADMIN_MAIL, '【川辺屋】' + subject, body); }
function fmt_(v, p) { return v instanceof Date ? Utilities.formatDate(v, TZ, p) : String(v || ''); }
/** 翌月のラベルを「2026年10月」の形式で返す(フォームの対象月と同じ形) */
function nextMonthLabel_() {
const d = new Date();
d.setDate(1); // 月末日での繰り上がり事故を防ぐ
d.setMonth(d.getMonth() + 1);
return Utilities.formatDate(d, TZ, 'yyyy年M月');
}
/** 名簿の在籍スタッフを {職員番号: 氏名} で返す */
function roster_() {
const v = ss_().getSheetByName(SHEET_ROSTER).getDataRange().getValues();
const r = {};
for (let i = 1; i < v.length; i++) {
const no = String(v[i][0]).trim();
if (no && String(v[i][3]).trim() === '在籍') r[no] = String(v[i][1]).trim();
}
return r;
}
/** 台帳から、対象月を提出済みの職員番号を集める */
function submitted_(month) {
const v = ss_().getSheetByName(SHEET_REQ).getDataRange().getValues();
const s = {};
for (let i = 1; i < v.length; i++) {
if (String(v[i][4]).trim() === month) s[String(v[i][2]).trim()] = true; // E列 対象月 / C列 職員番号
}
return s;
}
// ───── 5分おき:受け口 → シフト希望台帳 ─────
function importRequests() {
const ss = ss_();
const linked = ss.getSheetByName(SHEET_LINKED);
const ledger = ss.getSheetByName(SHEET_REQ);
if (!linked || linked.getLastRow() < 2) return;
const data = linked.getDataRange().getValues();
const head = data[0].map(String);
const c = {};
const missing = [];
Object.keys(H).forEach(function (k) {
c[k] = head.indexOf(H[k]);
if (c[k] < 0 && k !== 'NOTE') missing.push(H[k]);
});
if (missing.length) {
mail_('【要確認】受け口の見出しが見つかりません', '見出し「' + missing.join('/') + '」がありません。\n受け口の見出し: ' + head.join(' / '));
return;
}
// 取込済みキー(I列)
const done = {};
if (ledger.getLastRow() >= 2) {
ledger.getRange(2, 9, ledger.getLastRow() - 1, 1).getValues()
.forEach(function (r) { if (r[0]) done[String(r[0])] = true; });
}
const names = roster_();
const added = [];
for (let i = 1; i < data.length; i++) {
const r = data[i];
if (!r[c.AT]) continue;
const key = fmt_(r[c.AT], 'yyyy/MM/dd HH:mm:ss') + '|' + String(r[c.FNAME]).trim();
if (done[key]) continue;
const no = String(r[c.NO]).trim();
ledger.appendRow([
r[c.AT], r[c.FNAME], no, r[c.NAME], String(r[c.MONTH]).trim(),
r[c.OFF], r[c.TIME], c.NOTE >= 0 ? r[c.NOTE] : '', key
]);
done[key] = true;
added.push((names[no] ? names[no] : r[c.NAME] + '(名簿に無い番号 ' + no + ')') + '/' + r[c.MONTH] + '/休:' + r[c.OFF]);
}
if (added.length) mail_('シフト希望 ' + added.length + '件 受付', added.join('\n'));
}
// ───── 17日・21日の朝:提出状況を管理者へメール ─────
function reportShiftSummary() {
const month = nextMonthLabel_();
const names = roster_();
const sub = submitted_(month);
const notYet = [];
let total = 0;
Object.keys(names).forEach(function (no) {
total++;
if (!sub[no]) notYet.push(names[no]);
});
const final = new Date().getDate() >= 21;
const head = final ? '【最終集計】' : '【途中経過】';
mail_(head + month + ' シフト希望 提出 ' + (total - notYet.length) + '/' + total,
head + month + ' シフト希望\n'
+ '提出 ' + (total - notYet.length) + ' / ' + total + '名\n'
+ '未提出:' + (notYet.length ? notYet.join('、') : 'なし') + '\n\n'
+ (final ? '未提出の方には直接お声がけください。\n' : '本日10時のリマインド配信は、上の未提出の方に届きます。\n')
+ 'シフト希望台帳:' + ss_().getUrl());
}
// ───── 25日の朝:確定シフトを本人ごとの文面にまとめて管理者へ ─────
function buildShiftMessages() {
const ss = ss_();
const sheet = ss.getSheetByName(SHEET_SHIFT);
const msgSh = ss.getSheetByName(SHEET_MSG);
const shift = sheet.getDataRange().getValues();
const names = roster_();
const lines = {};
const rows = {};
for (let i = 1; i < shift.length; i++) {
if (shift[i][6]) continue; // G列に何か入っていれば処理済み
const no = String(shift[i][1]).trim(); // B列 職員番号
const day = fmt_(shift[i][0], 'M/d'); // A列 日付(日付型)
if (!lines[no]) { lines[no] = []; rows[no] = []; }
lines[no].push(day + ' ' + shift[i][3] + ' ' + shift[i][4] + '〜' + shift[i][5]);
rows[no].push(i + 1);
}
const out = [];
const body = [];
Object.keys(lines).forEach(function (no) {
const text = (names[no] || shift[rows[no][0] - 1][2]) + 'さん\n来月のシフトが確定しました。\n\n'
+ lines[no].join('\n') + '\n\n変更が必要な場合は25日中にご連絡ください。';
out.push([no, names[no] || '', text]);
body.push('■ ' + (names[no] || no) + '\n' + text);
rows[no].forEach(function (r) { sheet.getRange(r, 7).setValue('文面作成済'); });
});
msgSh.clearContents();
msgSh.getRange(1, 1, 1, 3).setValues([['職員番号', '氏名', '本文']]);
if (out.length) msgSh.getRange(2, 1, out.length, 3).setValues(out);
mail_('確定シフトの配信文 ' + out.length + '名分',
out.length
? '「配信文」シートのC列をコピーして、エルメの1:1チャット(またはLINE公式アカウントのアプリ)から各自に送ってください。\n\n' + body.join('\n\n')
: '未配信の行はありません。');
}
トリガーの設定
操作場所:Apps Script > トリガー > トリガーを追加
| 関数 | 種類 | タイミング | やること |
|---|---|---|---|
importRequests | 時間主導型 > 分ベース | 5分おき | 受け口 → シフト希望台帳。新しい回答は管理者にメール |
reportShiftSummary | 時間主導型 > 月ベース | 17日 午前8時〜9時 | 途中経過(提出○/未提出者名)をメール |
reportShiftSummary | 時間主導型 > 月ベース | 21日 午前9時〜10時 | 最終集計をメール |
buildShiftMessages | 時間主導型 > 月ベース | 25日 午前9時〜10時 | 確定シフトの本人宛て文面を作る(章7) |
17日のメールはリマインド配信(10:00)より前に届くので、管理者は「今日の催促が誰に飛ぶか」を先に知ったうえで、必要なら個別に一言添えられます。
届くメールの例
なぜ管理者のLINEではなくメールなのか
GASから管理者自身のLINEに送ることは技術的には可能ですが、LINE Developersでトークンを発行して管理する手間が増え、1通ずつ配信数も消費します。メールなら設定は1行で、失敗もほぼありません。届く場所を「毎日見る受信箱」に寄せるだけで、確認し忘れはなくなります。
同じ人が2回出しても二重に数えません
集計は職員番号で1人と数えます。シフトを組むときは、台帳をその人のいちばん新しい行で見てください(受付開始の案内にも「新しいほうが有効」と書いてあります)。
つまずきポイント:初回実行時の権限承認
GASを初めて実行すると「このアプリは確認されていません」という警告が出ます。自分で書いたコードなので、詳細を開いて続行して問題ありません。ただし手動で1回実行して承認を済ませておかないと、トリガーは動きません。設定したら必ず1度、手で実行してください。
7. 確定シフトを各自に届ける
シフトを組み終えたら、「確定シフト」シートに1勤務=1行で入力します。25日の朝、GASが各自の自分の勤務だけを抜き出した文面を「配信文」シートと管理者のメールに用意します。管理者はそれを1:1チャットに貼って送ります。全員分のシフト表を送らないので、他人の勤務が見えません。
手順
- 21日〜24日に「確定シフト」シートへ入力する(G列は空欄のまま)
- 25日9時台、GASの
buildShiftMessagesが未処理の行を職員番号ごとにまとめ、「配信文」シートに1人1行で書き、同じ内容を管理者にメールする。処理した行のG列には「文面作成済」が入る - 管理者は勤務時間内に、エルメの友だちリストで本人(表示名はシフト希望台帳のB列で分かる)を開き、「配信文」のC列を貼って送る
14名なら10分の作業です
「コピーして貼る」を14回くり返すだけなので、慣れれば10分かかりません。この10分を自動化するには、GASからスタッフ個人のLINEに送る必要があり、章1の制約に当たります。月1回の手作業として割り切るのが、いちばん壊れない形です。
他人の勤務が見えてもよい職場なら、組み上がったシフト表の画像やPDFを「スタッフ_在籍」で絞り込んだ一斉配信で送る方法もあります。こちらは手作業ゼロ・14通で済みます。
G列が二重作成を防ぎます
文面を作った行には「文面作成済」と書き込まれるため、もう一度実行しても同じ内容は出てきません。シフトを後から追加・変更したときは、変更後の行を追加してG列を空欄にしておけば、次回の実行で差分だけの文面ができます。元の行は消さず、G列に「取消」などと書いて残しておくと、いつ変えたかの記録になります(章9)。
つまずきポイント:日付が変な表示になる
A列の日付が文字列で入っていると、GASが「10/2」の形に直せません。A列は日付型のまま(2026/10/05 の形式)にしてください。逆にE列・F列の時刻は文字列型です。A列は日付型、E・F列は文字列型。ここを取り違えると表示が崩れます。
8. 費用と無料枠の注意
結論から言うと、スタッフ14名規模なら、この仕組みは無料のまま運用できます。
| かかるもの | 費用 |
|---|---|
| LINE公式アカウント(コミュニケーションプラン) | 0円/月200通まで |
| エルメ(フリープラン) | 0円(作れるフォーム数・リッチメニュー数などに上限あり) |
| Googleスプレッドシート・GAS | 0円 |
月に何通消費するかの試算(スタッフ14名の場合)
| 配信 | 宛先 | 通数 |
|---|---|---|
| 1日:受付開始の案内(予約配信) | 14名 | 14通 |
| 17日:未提出者へのリマインド(予約配信) | 実績3〜6名程度 | 約5通 |
| 17日・21日:集計通知 | 管理者へメール | 0通 |
| 25日:確定シフト(エルメの1:1チャット) | 14名 | 14通 |
| 合計 | 約33通/月 | |
無料枠200通に対して、使うのは約33通。余力は十分です。1人あたりの消費はおよそ2.4通なので、スタッフが60名を超えるあたりから200通が見えてきます。そのときはLINE公式アカウントのライトプランへの切り替えを検討してください。
ここを間違える人がとても多い
通数は「配信ボタンを押した回数」ではなく「配信回数 × 送った人数」で消費されます。14人に1回送れば14通です。スタッフからの受信は消費しませんが、フォーム回答後にトークへ送る自動返信は1回答につき1通消費します。章4でお礼を「完了画面」にしているのはそのためです。エルメの1:1チャットも1通ずつ消費します。LINE公式アカウントのアプリのチャットから送れば消費しないので、通数が気になる月は確定シフトの送信をそちらに寄せると14通節約できます。
プランの切り替え時期をもう少し詳しく知りたい方は、エルメの無料プランでどこまでできるか・有料に切り替えるべき3つの分岐点をご覧ください。
9. 見落とすとトラブルになるポイント
仕組みが動くことと、その運用が法的に耐えることは別の話です。シフト制で働く人がいる会社には、押さえておくべき点があります。
「シフトによる」とだけ書いた労働条件通知書は足りない
厚生労働省は「いわゆる『シフト制』により就業する労働者の適切な雇用管理を行うための留意事項」を公表しています(令和4年1月7日公表、令和8年6月19日改正)。ここでいう「シフト制」とは、労働契約の締結時点では労働日や労働時間を確定的に定めず、一定期間ごとに作成される勤務シフトなどで初めて具体的な労働日・労働時間が確定する形態を指します。
この留意事項では、始業・終業の時刻について、労働条件通知書等に単に「シフトによる」と記載するのでは足りないとされています。労働日ごとの始業・終業時刻を明記するか、原則的な始業・終業時刻を記載したうえで、一定期間のシフト表等と併せて明示することが求められます。
◆ 効率化のつもりで作った仕組みが、そのまま明示の記録にもなります
この設計では、25日に各自へ確定シフトを送り、その文面が「配信文」シートに、処理日がG列の「文面作成済」として残ります。送信したトークも、エルメの1:1チャットの履歴に残ります。いつ・誰に・どの勤務を通知したかが、記録として残るということです。口頭で伝えて壁に貼るだけの運用と比べて、あとから確認できる形になっています。
ただし、これだけで労働条件の明示義務を満たすかどうかは、契約書の記載内容と合わせた個別の判断が必要です。就業規則や労働条件通知書の記載も、あわせて見直しておいてください。
確定したシフトを、あとから会社都合で削らない
提出された希望と、会社が確定させたシフトは別物です。そして一度確定させたシフトを直前に減らすと、休業手当(労働基準法26条)の問題が生じる場合があります。「暇だから明日は来なくていい」を繰り返している会社は、ここを一度確認しておいたほうがよいでしょう。適用の可否は個別の判断が必要ですが、少なくとも「いつ確定させ、いつ変更したか」の記録は残しておくべきです。
この設計では、確定シフトシートに変更の履歴を残せます。行を書き換えるのではなく、変更後の行を追加してG列を空欄にする運用にしておけば、差分の文面が自動でできて、記録も残ります。
年次有給休暇の付与日数は、シフトの実績で決まることがある
留意事項では、所定労働日数を定めにくいシフト制の労働者について、雇入れから6か月の時点ではその6か月の実際の労働日数を2倍した日数を年間の所定労働日数とみなして年休の付与日数を決められるとしています。1年6か月以降は前年の実績を使います。確定シフトシートは「予定」の記録ですが、実績と突き合わせて年間の労働日数を数える土台にもなります。有給の申請をLINEで受ける設計は有給申請をLINEフォームで受ける方法にまとめています。
配布やリマインドは勤務時間内に送る
シフト希望の入力は数分とはいえ、会社からの業務上の指示です。深夜や休日に催促が届き、すぐ返す空気ができると、その時間が労働時間と評価される余地が出てきます。章4・章5の予約配信を勤務時間帯(例:10:00)にし、文面に「勤務時間内の入力で構いません」と添えているのはそのためです。
希望休の理由を、必要以上に集めない
フォームの備考欄は自由記入なので、スタッフが通院や家族の事情を書いてくることがあります。健康に関する情報は特に慎重な取り扱いが求められるため、「理由の記入は不要です」とフォームに明記しておくのが無難です。持たない情報は漏れません。
シフトの記録と、実際に働いた時間の記録は別物
確定シフトは「働く予定」の記録であって、「実際に働いた時間」の記録ではありません。労働時間の把握は、原則として客観的な記録によることとされています。シフト表だけで勤怠管理を済ませると、早出や残業が記録に残りません。
打刻の仕組みをどう設計すれば記録として耐えるかは、LINEで打刻した記録が労働時間の記録として認められるかで整理しています。なお打刻はGASで受信する必要があるため、エルメを使わない打刻専用の別のLINE公式アカウントで組みます。この記事のスプレッドシートに「打刻ログ」シートを足せば、台帳は1つにまとめられます。
10. 運用開始チェックリスト
公開前に、この表をひととおり確認してください。
| 確認項目 | 見る場所 |
|---|---|
| スタッフ全員に「スタッフ_在籍」が付き、人数が名簿の在籍人数と一致しているか | エルメ 友だちリスト |
| 名簿のD列が「在籍」で統一されているか(在職・在籍中などの表記ゆれがないか) | スプレッドシート |
| 確定シフトのE列・F列が「書式なしテキスト」になっているか | 表示形式の設定 |
| フォームの「対象月」が選択式で、選択肢が翌月の「2026年10月」形式になっているか | エルメ フォーム作成 |
| フォーム回答後に「シフト_提出済」タグが付く設定になっているか | エルメ フォーム作成 |
| フォームの回答後がトークへの自動返信ではなく完了画面になっているか | エルメ フォーム作成 |
フォームをスプレッドシート連携し、受け口のシート名を SHEET_LINKED に写したか | Apps Script |
| 配布(1日)とリマインド(17日)の予約配信が、どちらも「スタッフ_在籍 あり」の条件で勤務時間内の時刻になっているか | エルメ メッセージ配信 |
importRequests を手動で実行し、権限承認を済ませたか。テスト回答が台帳に写るか | Apps Script/台帳 |
| トリガーを4つ(5分おき・17日・21日・25日)設定したか | Apps Script トリガー |
reportShiftSummary を手動実行し、管理者のメールに集計が届くか | 管理者の受信箱 |
| スプレッドシートの共有権限が、必要な人だけに絞られているか | スプレッドシート 共有設定 |
| リッチメニューの「シフト希望」がスタッフにだけ表示され、お客様には出ないか | LINEアプリ(スタッフ用・客用の2台で確認) |
最後から2つ目が最重要
スプレッドシートには全スタッフの氏名と希望休が入っています。「リンクを知っている全員が編集可」になっていないか、必ず確認してください。閲覧できる人は、実務上必要な最小限に絞ります。
11. よくある質問
スタッフがフォームではなくトークで直接送ってきたらどうなりますか
受け口にも台帳にも入りません。その結果、未提出者として扱われてリマインドが飛びます。最初の1〜2か月は必ず起きるので、受付開始の案内に「メニューのボタンから送ってください」と毎回書いておいてください。3か月ほどで定着します。
GASでスタッフの返信を受け取ったり、スタッフに送ったりすることはできませんか
エルメを接続している間はできません。LINE公式アカウントのWebhookは1つしか設定できず、その枠をエルメが使っているためです。受信が必要な処理はすべてエルメのフォームや自動応答で受け、送信はエルメの配信や1:1チャットで行います。GASは「スプレッドシートを読み書きしてメールする」役に徹するのが、いちばん壊れない形です。
締切後に希望を変更したいと言われたら
フォームから再送してもらってかまいません。台帳には新しい行が追加されるだけで、古い行も残ります。GASは提出の有無しか見ていないので、集計には影響しません。管理者がシフトを組むときに、受付日時の新しい行を見ればよいことになります。
職員番号を間違えて送ってきたら
名簿に無い番号の回答は、GASの取込メールに「名簿に無い番号」と注記されて届きます。本人は提出したつもりでも集計上は未提出になるので、台帳のC列を正しい番号に直してください。直した時点で集計に反映されます。
スタッフが40人を超えたらどうなりますか
40人で計算すると、受付案内40通+リマインド15通+確定シフト40通で、およそ95通。無料枠200通の半分ほどで、まだ十分に収まります。60人を超えるあたりから枠が見えてくるので、その時点でLINE公式アカウントのライトプランを検討してください。確定シフトの送信をLINE公式アカウントのアプリのチャットに寄せれば、その40通は消費しません。
Googleアカウントは個人のもので大丈夫ですか
動きますが、おすすめしません。担当者が退職したときにスプレッドシートごと引き継げなくなります。会社としてのGoogleアカウントを1つ作り、そこにファイルを置いてください。
12. 応用アレンジ
同じ骨格のまま、次のように広げられます。いずれも「スタッフとのやりとりはエルメ、保存はスプレッドシート、取込と管理者への通知はGAS」の形は変わりません。
- 部署ごとに集計を分ける:名簿のC列(部署)で絞り込み、フロント担当と調理担当で別々に集計メールを送る。GASの集計に1行足すだけです。配布・リマインドも「部署_フロント」のようなタグを足せば部署別に送れます
- 締切前日のリマインドを足す:章5と同じ条件で「19日 10:00」の予約配信をもう1本作る。催促は月2回までにとどめます
- 急なシフト交代の募集:欠員が出たとき、「スタッフ_在籍」で絞り込んだ一斉配信で「10/9の遅番、入れる方はいますか」と送り、返信はフォームか1:1チャットで受ける。確定シフトシートを見れば、その日が休みの人は分かります
- 打刻ログと突き合わせる:打刻専用の別アカウントで集めた打刻ログを同じスプレッドシートに置き、確定シフトと実績の差を月末に自動集計する
- 予約の入り具合に合わせて人を配置する:予約が集中する曜日や時間帯が分かっていれば、シフトを組むときの判断材料になります。予約側をLINEで受ける設計は美容室の予約をLINEで自動化する設計図にまとめていて、同じスプレッドシートに並べれば繁忙の予測が立てやすくなります
- 現場ごとの作業報告も同じ骨格で集める:誰がどの現場に入ったかが分かると、翌月のシフトを組む精度が上がります。設計は現場報告をLINEで集める設計図にまとめています
- 有給の申請も同じ仕組みで受ける:ただし有給は希望休とは扱いが違い、年次有給休暇管理簿の作成義務や年5日の取得義務が関わるため、フォームは分けてください。設計は有給申請をLINEフォームで受ける方法にまとめています
いきなり全部を作らないでください。まずは章3〜5(タグ・フォーム・配布・リマインド)だけを1か月回してみる。それで回ることを確認してから、GASの集計と確定シフトの文面作成を足していくのが確実です。章5までで、催促の電話はすでになくなっています。
まとめ
シフト希望の回収は、エルメのフォームで受け、スプレッドシートに溜め、エルメの予約配信が未提出者だけに催促して、確定後は各自に自分の勤務だけを届ける。この4つがつながれば、探す作業も催促の電話も転記もなくなります。しかもスタッフ14名規模なら、月33通ほどで無料の範囲に収まります。GASはLINEに触らず、台帳への取込と管理者へのメールだけ。だから壊れにくく、引き継ぎも簡単です。
最初にやることは1つだけです。エルメで「スタッフ_在籍」タグを作り、スタッフにだけ登録用リンクを送ってください。 この柵さえできれば、あとの章はその上に積んでいくだけです。
同じ考え方で勤怠の記録まで組みたい方は、LINE打刻の記録が労働時間の記録として認められるかもあわせてご覧ください。どのツールで組むか迷っている段階であれば、Lステップとエルメの料金比較から先に読むと判断が早くなります。
まずはフォームを1つ作ってみる
この設計図は、エルメのフリープランで一通り試せます。初期費用0円、最低契約期間もありません。章4のシフト希望フォームを作るところから始めれば、30分で最初の1歩が終わります。









