2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

みやもとさんで記録した勤務時間をGASで人事労務freeeに転記する

Last updated at Posted at 2020-07-15

はじめに

私の勤務先では、 数年前より給与計算に人事労務freee、出退勤時刻の記録にオープンソースの「みやもとさん」を設置して使用しています。

freee人事労務を導入した当初は、freee公式の打刻APIが使用できないプランで契約していたのと、みやもとさん単体では、時間外労働時間の計算に対応できない課題がありました。そこで、 みやもとさんのGoogle SpreadSheetからGASで出退勤時間を取得し、人事労務freee APIを使って登録するGoogle Apps Scriptを作成しました。

  • これから新規でslackによる勤怠管理を導入するなら、freee公式の打刻Appを導入するほうが楽だと思います。
  • 既にみやもとさんを導入済みの場合に役立つかもしれません

処理の概要

slackで出勤時に「おはよう」、退勤時に「おつかれさまでした」とつぶやく → みやもとさんが Google SpreadSheetに出退勤時間を記録 → 今回作ったGoogle Apps Scriptを毎日午前2時に定時実行 →人事労務freeeに転記

前提

  • みやもとさんは設置済み
  • freee APIにアプリを登録して、APP IDとAPP SECRETを取得している
    • ものとします

実装

  1. Google Scriptのスクリプトエディタを開き、コードをコピーします。
  2. スクリプトのプロパティに秘匿情報を追加します。
  • APP_ID freee APIのAPP_ID
  • SECRET freee APIのSECRET
  • EMPLOYEE シート名と従業員IDの対応をJSONで記述 例){"kkanazaw": "12345"}
  • MIYAMOTO_SAN_ID みやもとさんのGoogle SpreadSheetのid
  • COMPANY_ID freeeの企業ID
  1. スクリプトを定期実行するトリガーを設定をします

OAuth2認証の部分は【freee API】GASを用いてGoogleスプレッドシートと連携する – freee ヘルプセンターのサンプルコードを使用しています。

function myFunction() {
  var company_id = PropertiesService.getScriptProperties().getProperty("COMPANY_ID");
  // スクリプトのプロパティEMPLOYEEに連携するシート名とfreeeの従業員IDの対応をJSONで記述する
  // シート名:freeeの授業員ID
  // シート名とfreeeの授業員IDは紐付いていないため、手動で設定しないといけない。
  // 従業員IDはFreeeの従業員一覧APIから取得できる。
  // APIリファレンスのページで実際にAPIを叩けるので、
  // getAccessToken()でaccesstokenを取得し、company_id,年,月のパラメータを指定して実行する。
  // API
  // https://developer.freee.co.jp/docs/hr/reference#/%E5%BE%93%E6%A5%AD%E5%93%A1/index2
  var config = JSON.parse(PropertiesService.getScriptProperties().getProperty("EMPLOYEE"));

  for (var key in config) {
    var emp_id = config[key];
    var today = new Date();
    var year = Utilities.formatDate( today, 'Asia/Tokyo', "yyyy");
    var month = Utilities.formatDate( today, 'Asia/Tokyo', "MM");
  
    var spreadsheet = SpreadsheetApp.openById(PropertiesService.getScriptProperties().getProperty("MIYAMOTO_SAN_ID"));
    var sheet = spreadsheet.getSheetByName(key);
    var data = sheet.getDataRange().getValues();
    for(i = 1; i <= 31; i++) {
      var latest = data[data.length-i];
      var date = latest[0];  
      var start = latest[1];
      var end = latest[2];
      if(date && start && end) {
        putRequest(company_id, emp_id, date, start, end);
        Utilities.sleep(1000);
      }
    }    
  }
}


/******************************************************************
参照ライブラリ
title        |OAuth2
project_key  |1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF
******************************************************************/

//連携アプリ情報(Googleスプレッドシートサンプルファイル)
var APP_ID = PropertiesService.getScriptProperties().getProperty("APP_ID");
var SECRET = PropertiesService.getScriptProperties().getProperty("SECRET")

/******************************************************************
function name |alertAuth
summary       |認証のエンドポイントとなるダイアログ
******************************************************************/
function alertAuth() {
  var service = getService();
  var authorizationUrl = service.getAuthorizationUrl();
  console.log(authorizationUrl);
}

/******************************************************************
function name |getService
summary       |freeeAPIのサービスを取得
******************************************************************/
function getService() {
  return OAuth2.createService( "freee" )
      .setAuthorizationBaseUrl( "https://accounts.secure.freee.co.jp/public_api/authorize" )
      .setTokenUrl( "https://accounts.secure.freee.co.jp/public_api/token" )
      .setClientId( APP_ID )
      .setClientSecret( SECRET )
      .setCallbackFunction( "authCallback" )
      .setPropertyStore( PropertiesService.getUserProperties() );
}

/******************************************************************
function name |authCallback
summary       |認証コールバック
******************************************************************/
function authCallback( request ) {
  var service = getService();
  var isAuthorized = service.handleCallback( request );
  if ( isAuthorized ) {
    return HtmlService.createHtmlOutput( "認証に成功しました。タブを閉じてください。" );
  } else {
    return HtmlService.createHtmlOutput( "認証に失敗しました。" );
  };
}

function putRequest(company_id, emp_id, date, start, end) {
  
  var freeeApp = getService();
  var accessToken = freeeApp.getAccessToken();
  var requestUrl = "https://api.freee.co.jp/hr/api/v1/employees/" + emp_id + "/work_records/" + Utilities.formatDate( date, 'Asia/Tokyo', 'yyyy-MM-dd');
  var headers = {"Authorization" : "Bearer " + accessToken };
  var requestBody = {
  "company_id": company_id,
  "break_records": [
    {
      "clock_in_at": Utilities.formatDate( date, 'Asia/Tokyo', "yyyy-MM-dd'T''12:00:00.000'Z"),
      "clock_out_at": Utilities.formatDate( date, 'Asia/Tokyo', "yyyy-MM-dd'T''13:00:00.000'Z")
    }
  ],
  "clock_in_at": Utilities.formatDate( start, 'Asia/Tokyo', "yyyy-MM-dd'T'HH:mm:ss.000Z"),
  "clock_out_at": Utilities.formatDate( end, 'Asia/Tokyo', "yyyy-MM-dd'T'HH:mm:ss.000Z")
  };
  
  diff_time = (end.getTime() - start.getTime()) / (1000 * 60 * 60);
  if(diff_time < 6.0) {
    delete requestBody["break_records"];
  }
  
  var options = {
    "method":"PUT",
    "headers":headers,
    'contentType': 'application/json',
    "payload":JSON.stringify(requestBody),
    muteHttpExceptions: true
  };
  var res = UrlFetchApp.fetch( requestUrl , options );
  console.log(res);
}

// debug用
// APIリファレンスの画面でemp_idやcompany_idを取得したいときに使う
// https://developer.freee.co.jp/docs/hr/reference
function getAccessToken() {
  var freeeApp = getService();
  console.log(freeeApp.getAccessToken());
}

実行結果

みやもとさんのGoogle SpreadSheetに記録された出退勤時間が

Image

スクリプト実行後に、前日までの出退勤時刻がFreee側に転記されます。

- 時間外労働時間があった場合も、Freee側で計算されます。
Image

課題

  • 毎回過去31日分の勤怠を更新しています
    • みやもとさん側で過去の勤怠記録を編集した場合や、たまたまスクリプトがエラーになった場合に備え、翌日の処理で更新できるようにしています。そのためやや冗長な書き方になっています。
    • 従業員数が増えた場合は、APIのリクエスト数の調整が必要です。
  • シート名と従業員IDの登録部分
    • 人事労務freeeに登録しているメールアドレスと、slackのアドレスが違うケースがあるため、手動でドキュメントプロパティに登録しています。
      • アドレスを統一できるなら、従業員APIから自動的に設定できると思います。
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?