技術は私たちの力。技術は私たちの楽しみ。 Creative Developer BLOG 技術部ブログ
Technology is our strength. Technology is what we enjoy.

GASとZapierで作る勤怠リマインド自動化システム【コード例つき】

2025-07-22 勉強会
こんにちは、インターパーク人事の眞鍋です。

前回ノリで参加した技術勉強会でお褒めの言葉を頂いてしまったばかりに調子にのって第二回の発表です。前回は、自社サービスである「サスケWorks」とGoogle Apps Script(GAS)、Looker Studioを連携させた採用管理システムの構築について発表しました。

今回はその知見を活かして、勤怠リマインドの自動化に取り組みました。

同じ悩みを持っているであろう世の中の人事担当者に何かヒントになればいいなと思い、コピペで再現できるようコードも載せています。

↓前回の記事↓

【技術ブログ】ノーコード×API×GAS×データポータルを活用してオリジナルの採用管理アプリを作った

https://note.com/interpark_hr/n/na201e14b7d76

勤怠リマインドの苦悩

勤怠システムにはリマインド機能が備わっていますが、それでも漏れが出てしまうのが人間というものです。

月初にエラーをまとめて「申請漏れてます」とリマインドする作業はこれまでほとんど手動で行ってきました。社員数が少ないうちはそれでも対応可能でしたが、組織の拡大につれて徐々に負担が大きくなってきました。リマインドという性質上、スルーされてしまうこともあり小さなストレスが溜まる作業でした。

リマインドされるのが好きな方はいないと思いますが、する方も嫌なんです。
ならば自動化してしまおう!ということで取り組むことにしました。

今回の取り組みの全体像

今回の取り組みの全体像はこちらです。

・月末月初に勤怠申請・承認のアナウンスを自動送信
・勤怠システムから該当月の勤怠データのCSVをダウンロードしスプレッドシートに貼り付け、関数でエラーを抽出
・スプレッドシートにある実行ボタンをトリガーにZapierが発動し、Google Chatで個別にリマインド連絡

この仕組みにより、作業時間と精神的負担を大幅に短縮することができました。
完全自動化も可能ではありますが、通知内容の最終確認を行うために実行ボタンによる手動トリガーを残しています。

次から具体的にどのように実装していくかを書いていきます。

申請・承認のアナウンス

いきなり大量の勤怠リマインドが来てびっくりしてしないようにリマインド予告botを作ります。

毎月の最終営業日前日と第一営業日に申請・承認のアナウンスを自動で行ってもらうようにGASでbotを実装しました。営業日かどうかを祝日カレンダーで判定し、自動的に該当日の10:00に通知トリガーを登録します。

※最上部のWebhookを変更してGASに貼り付けするだけですぐ使えるようになります※
const WEBHOOK_URL = 'googlechatのwebhookを挿入';
const HOLIDAY_CALENDAR_ID = 'ja.japanese#holiday@group.v.calendar.google.com';

function getBusinessDays(year, month) {
const cal = CalendarApp.getCalendarById(HOLIDAY_CALENDAR_ID);
return Array.from({length: 31}, (_, i) => new Date(year, month, i + 1))
.filter(d => d.getMonth() === month && d.getDay() !== 0 && d.getDay() !== 6 &&
cal.getEvents(new Date(d.setHours(0,0,0,0)), new Date(d.setHours(23,59,59,999))).length === 0);
}

function delTrigger(fn) {
ScriptApp.getProjectTriggers().forEach(t => t.getHandlerFunction() === fn && ScriptApp.deleteTrigger(t));
}

function sendToChat(msg) {
if (!msg?.trim()) return;
UrlFetchApp.fetch(WEBHOOK_URL, { method: 'post', contentType: 'application/json', payload: JSON.stringify({ text: msg }) });
}

function sendReminderBeforeMonthEnd() {
delTrigger('sendReminderBeforeMonthEnd');
sendToChat("\nお疲れ様です。\n今月の最終営業日が近づいていますので、各自残業、有休、シフト変更などの申請漏れが無いかご確認ください。\nhttps://id.jobcan.jp/users/\n\n月末に確認してほしいこともまとめています。\nhttps://wiki.interpark.co.jp/649e6764be923dd12c32331b");
}

function sendReminderAtMonthStart() {
delTrigger('sendReminderAtMonthStart');
sendToChat("\nお疲れ様です。\n本日より新しい月のスタートです☀️\n13時に勤怠のリマインドを行いますので、各自申請・承認をお願いいたします。");
}

function setReminderTrigger(fn, date) {
delTrigger(fn);
date.setHours(10, 0, 0, 0);
ScriptApp.newTrigger(fn).timeBased().at(date).create();
}

function setNextMonthTriggers() {
const today = new Date(), y = today.getFullYear(), m = today.getMonth();
const currentBizDays = getBusinessDays(y, m);
if (currentBizDays.length >= 2) setReminderTrigger('sendReminderBeforeMonthEnd', currentBizDays.at(-2));

const nextBizDays = getBusinessDays(y, m + 1);
if (nextBizDays.length >= 1) setReminderTrigger('sendReminderAtMonthStart', nextBizDays[0]);
}
すると最終営業日の2日前にこんなチャットが飛ぶようになります。
勤怠システムのURLとチェックポイントをまとめた社内wikiを添えてます。

第一営業日にはこんなチャットが飛ぶようになります

エラーの抽出&送信

勤怠データの貼り付け元になるスプレッドシートを作成します。
スプレッドシートは3つのタブで構成しています。

①従業員とWebhookの一覧をまとめるシート

Google Chatでメッセージを送るために、チャットスペース毎にWebhook URLを発行します。Google Chatの「アプリと統合」からWebhookを発行し、それぞれ対応する従業員や部署と紐付けて管理しています。

②勤怠データを貼り付けるシート

勤怠データ貼り付けると予め入れておいた関数でエラーフラグをたてます。

インターパークの場合は打刻漏れ、シフト申請漏れ(複数シフトから自由に選べるため、シフトと打刻がズレると早退/遅刻フラグがたつ)、残業申請漏れの3つをここで出しています。

A~J列が勤怠システムからコピペした勤怠データで、M~L列に関数を入れてエラーフラグを立てています。

※従業員名はダミーデータです※

③エラーメッセージを成型してZapierにトリガーを送信するシート

抽出されたエラーをもとに、個別・管理職向けにエラーを整理します。

H~Jには以下の関数を入れています。
エラーが出てる人の名前、Webhook、エラー一覧を抽出しています。

/H3の関数
=UNIQUE(FILTER('貼り付け'!R2:R, '貼り付け'!S2:S <> ""))

/I3の関数
=XLOOKUP(H3, '従業員一覧'!B:B, '従業員一覧'!C:C, "")

/J3の関数
=LET(
name, H3,
filtered, FILTER({'貼り付け'!Q2:Q, '貼り付け'!S2:S}, '貼り付け'!R2:R = name),
IF(COUNTA(filtered),
name & CHAR(10) &
TEXTJOIN(CHAR(10), TRUE,
MAP(
INDEX(filtered,,1), INDEX(filtered,,2),
LAMBDA(d, e, TEXT(d, "yyyy/mm/dd") & " " & e)
)
),
""
)
)
送信したいエラーを出せたのでGoogle Chatに送る準備をしていきます。

以下のGASコードは、スプレッドシートからZapierのCatch Hookにデータを送信する役割を担います。Zapierはこの情報を受け取り、Google ChatにPOSTします。

function sendToZapierViaCatchHook() {
 const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("メッセージ");
 const lastRow = sheet.getLastRow();

 const zap1CatchHook = "※zapier hookを挿入";
 const data1 = sheet.getRange(2, 9, lastRow - 1, 2).getValues(); // I:J
 data1.forEach(([chatWebhook, message], i) => {
   if (!chatWebhook || !message) return;
   postToCatchHook(zap1CatchHook, chatWebhook, message, i + 2, "Zap1");
 });

 const zap2CatchHook = "※zapier hookを挿入";
 const data2 = sheet.getRange(2, 13, lastRow - 1, 2).getValues(); // M:N
 data2.forEach(([chatWebhook, message], i) => {
   if (!chatWebhook || !message) return;
   postToCatchHook(zap2CatchHook, chatWebhook, message, i + 2, "Zap2");
 });
}

function postToCatchHook(catchHookUrl, chatWebhookUrl, message, row, label) {
 try {
   const payload = {
     webhook: chatWebhookUrl.toString().trim(),
     text: message.toString()
   };
   UrlFetchApp.fetch(catchHookUrl, {
     method: "post",
     contentType: "application/json",
     payload: JSON.stringify(payload)
   });
   Logger.log(`✅ ${label} 送信成功: Row ${row}`);
 } catch (e) {
   Logger.log(`❌ ${label} 送信失敗: Row ${row} - ${e}`);
 }
}



スプレッドシートの設定はこれで完了です。
次にZapierの設定を進めていきます。

Zapierとは?

Zapier(ザピアー)とは、Webサービスやアプリの作業を自動化できるサービスです。例えば「Googleフォームに回答が届いたらSlackに通知」「スプレッドシートにデータが入ったらメールを自動送信」みたいな日々の繰り返し作業を自動化できます。

つまり、「Aが起きたらBをする」を、プログラムを書かずに誰でも作れるツールです。今回は「Webhooks by Zapier」の Catch Hook で、外部のシステム(今回はGAS)から送られてくるデータを受け取ったら「Webhooks by Zapier」の Custom Request を使って、Google ChatのWebhook URLにPOSTする、というシンプルな作りにしています。

個別・管理職それぞれにZapを作成し設定します。

ここまでで準備が完了しました。

リマインドの時間だ!!

勤怠システムから勤怠データをダウンロードして貼り付けをすると個別のエラーと・管理職向けに課員のエラー一覧が出力され、実行ボタンを押すと対象者にリマインドメッセージが行くようになりました!

おわりに

複雑な関数やGASを書いているので慣れてない人から見るとハードルが高く感じるかもしれないですが、今回自分で一行も書いていません。
ロジックを考えてAIにコードを書いてもらい実行した結果を基に修正指示をする…というサイクルで改善を繰り返して運用まで乗せれるレベルまで持ってくることができました。

ちなみに、AI100%でコードを作成する際に知らないうちに不要な機能を追加されてしまったりするのでコード生成時の条件に下記を追加すると良くなってる気がしました。
#コーディング原則
・YAGNI(You Aren't Gonna Need It):今必要じゃない機能は作らない
・DRY(Don't Repeat Yourself):同じコードを繰り返さない
・KISS(Keep It Simple Stupid):シンプルに保つ

技術に明るくなくてもたいていのことは自動化できてしまう管理部門としては良い時代になったものです。今後も自動化を進めて明るいうちからビールを飲む生活に移行したいと思います。
本記事が、同様の課題を抱える人事・労務担当者の方々にとって、少しでも参考になれば幸いです。
記事一覧へ

New!

Member

システム部開発ユニット
クリエイティブ戦略部デザインユニット
クリエイティブ戦略部プランニングユニット
管理部