
SmartHR APIとGoogle Apps Scriptを使って社内向け社員一覧の取得を自動化する
社員一覧のマスターをどこに置くか
社内の人事情報ってどこで管理していますか?中小企業は多くの場合、部門毎にエクセルとかスプレットシートで肩書きや連絡先を管理していたり、人事や労務担当が別に住所などの詳細なデータを管理していたりしませんでしょうか。その結果、いろんな部署で手作業による人事情報の更新がされたりされてなかったりして、どれが最新か、誰が管理に責任を持つのか、誰にどこまで見せてよいのか、などなど、色々課題があったりします。
SmartHRのデータをマスターとする
今回当社では、SmartHRの情報を1次情報とすると改めて決めました。これは、入社時に全て正確な情報を漏れなく記入することと、入社後に部署や肩書きが変わった際にも、必ず人事労務チームがメンテナンスする役割を持つことで、全社員が常に最新で正確な情報にアクセスできることを保証することです。ここで一つ課題が出てきます。人事労務情報は社員の個人情報そのものなので、エクスポートして丸っと共有することはできません。かといって、毎月手作業で社内共有用のリストを手作業で更新するのも嫌です。そこで、APIを使うことにしました。
Google Apps Script
当社では、Google Workspaceが全社導入されていて、社員のみが閲覧できるドキュメントを簡単に設定することができます。そこで、Google スプレッドシート内でGAS(Google Apps Script)を活用して、SmartHR APIから取得した社員リストを毎日自動でシートに反映するコードを書いてみました。
Google スプレッドシート
スプレッドシートのメニューから、「ツール」 > 「スクリプトエディタ」を選択して、GASファイル内のメソッドを記述します。
Employee.gs
実際に動くコードはこちらです。(別途プロパティ設定が必要)
function listEmployee() { | |
//SmartHRのAPIキー(機密情報なのでプロパティで事前設定しておく) | |
const TOKEN = PropertiesService.getScriptProperties().getProperty("SMARTHR_APIKEY"); | |
//組織名(SmartHRで登録されているサブドメイン名) | |
const ORG = PropertiesService.getScriptProperties().getProperty("ORG"); | |
//一覧に表示するドメイン名(Gmailなどの個人アドレスを表示させないため) | |
const DOMAIN = PropertiesService.getScriptProperties().getProperty("DOMAIN"); | |
//シート名 | |
const SHEET = PropertiesService.getScriptProperties().getProperty("SHEET"); | |
//何ページ分取得するか(登録されている社員数に依存する) | |
const LAST_PAGE = 7; | |
//1ページ最大100件まで | |
const PER_PAGE = 100; | |
//入社後何日後に一覧表示するか(入力未完で表示させないために) | |
const DATE_GAP = 1; | |
// 現在アクティブなスプレッドシートを取得 | |
const ss = SpreadsheetApp.openById(SpreadsheetApp.getActiveSpreadsheet().getId()); | |
// シート名の指定 | |
const sheet = ss.getSheetByName(SHEET); | |
//既存データの最終行の取得 | |
const lastRowNum = sheet.getLastRow(); | |
//空でも消せるように1件追加 | |
sheet.appendRow([""]); | |
//スプレッドシートに記載されている既存データの全件行削除 | |
sheet.deleteRows(1, lastRowNum); | |
//1行目のヘッダ挿入 | |
sheet.appendRow(["社員番号","姓","名","セイ","メイ","入社日","Eメール","部署1","部署2","部署3","役職","業務"]); | |
//読み込んだ社員情報全件をフィルタや加工して一覧に追記する。 | |
for(var page=1;page<=LAST_PAGE;page++){ | |
const endpointUrl = "https://"+ORG+".smarthr.jp/api/v1/crews?page="+page+"&per_page="+PER_PAGE+"&access_token="+TOKEN; | |
const response = UrlFetchApp.fetch(endpointUrl); | |
const json = JSON.parse(response.getContentText()); | |
for(var i=0;i<PER_PAGE;i++){ | |
//Logger.log(json[i]); | |
if(json[i]!=null){ | |
let last_name = json[i]["last_name"];; | |
let first_name = json[i]["first_name"]; | |
let last_name_yomi = json[i]["last_name_yomi"]; | |
let first_name_yomi = json[i]["first_name_yomi"]; | |
const emp_code = json[i]["emp_code"]; | |
const business_last_name = json[i]["business_last_name"]; | |
const position = json[i]["position"]; | |
const occupation = json[i]["occupation"]; | |
const entered_at = json[i]["entered_at"]; | |
const resigned_at = json[i]["resigned_at"]; | |
const department = json[i]["department"]; | |
//ビジネスネームがあればそれを優先する | |
if(business_last_name != ""){ | |
last_name = json[i]["business_last_name"]; | |
first_name = json[i]["business_first_name"]; | |
last_name_yomi = json[i]["business_last_name_yomi"]; | |
first_name_yomi = json[i]["business_first_name_yomi"]; | |
} | |
//部署名を分割して配列にする | |
let departmentList = null; | |
departmentList = getDepartmentList(department); | |
//会社ドメインのメールのときのみ表示する。 | |
let email = json[i]["email"]; | |
if(!email.includes("@"+DOMAIN)){ | |
email = null; | |
} | |
//社員コードが発行されていないときは-にする。 | |
if(emp_code == null){ | |
emp_code = "-"; | |
} | |
//入社直後は必要情報が十分に記入されていない(ビジネスネームなど)ことが多いため、本日+バッファ日数分を過ぎた人のみを表示する。 | |
const date0 = new Date(entered_at+"T00:00:00.000"); | |
const today = new Date(); | |
//入社済みかつ指定日数過ぎていたら表示する | |
if(isBefore(date0,today,DATE_GAP)){ | |
if(resigned_at == null){ | |
//各種条件を満たした社員情報が一覧に追加される | |
sheet.appendRow([emp_code,last_name,first_name,last_name_yomi,first_name_yomi, | |
entered_at,email,departmentList[0],departmentList[1],departmentList[2],position,occupation]); | |
} | |
} | |
} | |
} | |
} | |
//日付のカラムで昇順ソートする | |
const lastRow = sheet.getLastRow(); | |
const lastCol = sheet.getLastColumn(); | |
sheet.appendRow([""]); | |
const data = sheet.getRange(2,1,lastRow,lastCol); | |
data.sort({column: 1, ascending: true}); | |
//一覧作成の履歴をシートに記入する | |
const updateSheet = ss.getSheetByName("更新履歴"); | |
const date = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM-dd HH:mm:ss'); | |
updateSheet.appendRow([date]); | |
//日付の比較 | |
//a+c<b a+cとbの日付を比較してbのほうが大きいときにtrueを返す | |
function isBefore(a,b,c){ | |
a.setDate(a.getDate()+c); | |
const a1 = a.getTime(); | |
const b1 = b.getTime(); | |
if(a1 < b1){ | |
return true; | |
}else{ | |
return false; | |
} | |
} | |
//部署名をスラッシュやスペース区切りで分割する(SmartHRでどのような部署文字列で登録をするかに依存する) | |
function getDepartmentList(department){ | |
let departmentList = ["","",""]; | |
if(department!=null){ | |
departmentList = department.split("/"); | |
if(departmentList!=null){ | |
if(departmentList.length>1){ | |
if(departmentList[0]==null)departmentList[0]=""; | |
if(departmentList[1]==null)departmentList[1]=""; | |
if(departmentList[2]==null)departmentList[2]=""; | |
}else{ | |
departmentList = department.split(" "); | |
if(departmentList!=null){ | |
if(departmentList[0]==null)departmentList[0]=""; | |
if(departmentList[1]!=null)departmentList[1]=department.substring(departmentList[0].length+1,department.length); | |
} | |
} | |
} | |
} | |
return departmentList; | |
} | |
} |
プロパティ情報
個別情報は事前にGAS内のプロパティに設定しました。GASの新しいエディタでは、GUIによる設定ができなくなっているようですので、一時的に旧バージョンのエディタに戻してから設定しています。(コードからも設定できますが、コードから分離したかった)
トリガー起動
毎日や毎週の決まった時間に起動することができます。GASのメニューからトリガーを選んでどのタイミングでメソッドを起動するか指定します。今回は、毎日深夜に起動するようにしました。
SmartHRの設定
SmartHRのAPIキー(アクセストークン)を発行するためには、SmartHRの管理者権限を使って、APIキーに対応する認可リストを編集します。実はここが最も難易度が高いと思っていて、SmartHRには過去を含む全社員の個人情報が満載なので、社内のエンジニアに気軽に管理者権限を渡せないのです。かといって、人事労務チームに認可リスト作成をお願いするのは気が引ける。今回は、会長という特権を活かして、管理者権限を使ってAPIキー発行し、必要最低限の認可リストを作成しました。会長がコードを書けば良いのです。
管理者メニューから「外部システム連携」を選択して「アクセストークン」を新規発行します。
作成の際に必要になるのが、認可リストです。いくら管理者権限だからといっても、API経由で何でも取れてしまってはリスクが高いので、必要最小限の属性のみを指定しました。新規発行されたAPIキーは一度表示されると二度と見えなくなりますので、1Passwordなどの機密情報管理ソフトウェアなどを使って保管しておいてください。
社員一覧作成の工夫
社内向けの社員一覧を作成するにあたって工夫した点をいくつかご紹介したいと思います。
- ビジネスネームを優先的に表示する。(本名の掲載を望まない人も多い)
- 退職者は表示しない。
- 入社前の社員は表示しない。(入社前にSmartHRに登録されていることが多い)
- 個人のメールアドレスは表示しない。(入社前に連絡用の個人アドレスが登録されていることが多い)
- 部署名やチーム名を表示する。(特定の部署やチームをフィルタできるようにした)
まとめ
今回は、今まで社内で手作業で更新されていたり、様々な部署で似たようなことをやっている作業について、情報の一元化と更新の役割を明確にし、APIを通じた情報収集と適切なフィルタ処理を加えて、処理を自動化しました。毎週5分ぐらいの作業だったのかもしれませんが、こういった細かい社内業務をできるだけコンピュータに任せようとすることで、同時に業務フローや役割が明確になり、全社的な生産性が上がるのではと思っています。今後も、こういった細かい社内プロセスの整備と、省力化や自動化に注力していきたいですね。会長だしさ。