フルリモート時給制で働く自分のために打刻システムっぽいものをスプレッドシートで簡単に作ってみた
背景
- 普段、自分は時給制でクライアントの開発支援をしている(フルリモート)
- 稼働する時間帯はある程度自由にさせていただいている(平日の日中以外に、夜間や土日祝日も時間が取れれば稼働している)
- 稼働状況はSlackで共有し、自分が今稼働中か否かがわかるようにしている
- 時給制なのできっちりと自分が稼働している時間をスプレッドシートで記録している
- 始業や離席、着席、終業のタイミングで、Slackで報告し、時間を記録したり休憩時間を計算するのが結構面倒くさい
つくったもの
上のような、スプレッドシート上でボタンを押すと打刻できるものを作りました。自分しか使う予定がないので見にくいところは多々あると思いますがご容赦ください。
ボタンを押すと、スプレッドシートに時間を記録し、Slackにもメッセージが通知されます。例えば、始業ボタンを押すと、始業時間を記録し、Slackに「始業しました。」と通知が飛びます。また、昼休憩などに離席→着席する場合は、着席ボタンを押したタイミングで休憩時間が加算されます。
最初の案として、Slackで「稼働開始します。」などの言葉をトリガーにスプレッドーシートで時間を記録してもいいなと考えていました。しかし、"Slackの画面を開くだけで自分宛のメンションや参加しているスレッドが気になってしまう"、"「稼働開始します。」と言いたいだけなのにそれ以前のメッセージに既読をつけたくない"、"そもそも「稼働開始します。」って打つこと自体が手間"と感じ、スプレッドシート上でボタンを押すことをトリガーにしました。
できること
- 始業、離席、着席、終業それぞれのタイミングに適したボタンを押すことで、スプレッドシートへの打刻とSlackへの通知が可能
- 打刻することで始業時間、終業時間、休憩時間を記録可能
利用フロー
- 始業時に始業ボタンを押す
- 昼休憩などの離席時に離席ボタン、離席から戻ったら着席ボタンを押す
- 終業時に終業ボタンを押す
実装方法
ご参考程度に記載しておきます。変数名などわかりにくいかもしれませんがご容赦ください。
スプレッドシートの設定
スプレッドーシートの設定でタイムゾーンをTokyo
にしてください。(Tokyoではない場合は適宜変更してください。)
GASの設定
appsscript.jsonでタイムゾーンをAsia/Tokyo
にしてください。(Tokyoではない場合は適宜変更してください。)
Script定義
getRange
で何を参照しているかなど、処理内容は本記事に貼った画像と照らし合わせてご確認ください。
const SLACK_WEBHOOK_URL = YOUR_WEBHOOK_URL
const postSlack = (text) => {
var options = {
"method" : "POST",
"headers": {"Content-type": "application/json"},
"payload" : '{"text":"' + text + '"}'
};
UrlFetchApp.fetch(SLACK_WEBHOOK_URL, options);
}
// 時間の差分を[分]単位で返す
const calcTimesDiffMin = (fromDate, toDate) => {
const leaveTimeMs = toDate - fromDate
const leaveTimeMin = Math.floor(leaveTimeMs/1000/60)
return leaveTimeMin
}
// 始業
const start = () => {
const startDate = new Date()
const startDateString = Utilities.formatDate(startDate, 'Asia/Tokyo', 'MM/dd')
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
const dateColumnVals = sheet.getRange('H:H').getValues()
// 始業時間を記録する行番号を取得する
const nextRowNum = dateColumnVals.filter(String).length + 1
// 日付、始業時間、休憩時間(初期値)を記録する
sheet.getRange(`H${nextRowNum}`).setValue(startDateString)
sheet.getRange(`I${nextRowNum}`).setValue(startDate)
sheet.getRange(`K${nextRowNum}`).setValue("00:00")
postSlack("始業しました。")
}
// 離席
const leave = () => {
const leaveDate = new Date()
const leaveDateString = Utilities.formatDate(leaveDate, 'Asia/Tokyo', 'MM/dd')
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
const dateColumnVals = sheet.getRange('D:D').getValues()
// 離席時間を記録する行番号を取得する
const nextRowNum = dateColumnVals.filter(String).length + 1
// 日付、離席時間を記録する
sheet.getRange(`D${nextRowNum}`).setValue(leaveDateString)
sheet.getRange(`E${nextRowNum}`).setValue(leaveDate)
postSlack("離席しました。")
}
// 着席
const sit = () => {
const sitDate = new Date()
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
const sitColumnVals = sheet.getRange('F:F').getValues()
// 着席時間を記録する行番号を取得する
const nextRowNum = sitColumnVals.filter(String).length + 1
// 着席時間を記録する
sheet.getRange(`F${nextRowNum}`).setValue(sitDate)
// 休憩時間を加算する(離席時間と着席時間の差分を休憩時間として加算する)
const startDateColumnVals = sheet.getRange('H:H').getValues()
const startDateLastRowNum = startDateColumnVals.filter(String).length
const restTime = sheet.getRange(`K${startDateLastRowNum}`).getValue()
const leaveDate = sheet.getRange(`E${nextRowNum}`).getValue()
const leaveMin = calcTimesDiffMin(leaveDate, sitDate)
restTime.setMinutes(restTime.getMinutes() + leaveMin)
sheet.getRange(`K${startDateLastRowNum}`).setValue(restTime)
postSlack("着席しました。")
}
// 終業
const end = () => {
const endDate = new Date()
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
const endColumnVals = sheet.getRange('J:J').getValues()
// 終業時間を記録する行番号を取得する
const nextRowNum = endColumnVals.filter(String).length + 1
// 終業時間を記録する
sheet.getRange(`J${nextRowNum}`).setValue(endDate)
postSlack("終業しました。")
}
ボタンの設置
- スプレッドシートの[挿入]メニューで[図形描画]を選択し、図形を挿入する
- 挿入した図形にスクリプトを割り当てる(始業ボタンだったら
start
を割り当てる)
作った感想
簡単ですが、上記のように打刻システムっぽいものを作ることで無駄な時間消費やストレスから解放されました。最初はカードリーダーを使って本格的に作ってみても面白そうとか考えましたが、今のところは簡単に作ったこの手法で様子を見ていこうと思ってます。
さいごに
自分はこんな感じで実現しましたが、他にもっといい方法や簡単な方法があるとか、無料でこんなサービスがあるからオススメだよとかあれば是非、コメントで教えてください!
Discussion