【業務効率化の事例紹介】kintone と Googleカレンダーを連携させる

2020年6月29日IT活用支援G Suite, Google API, IT, ITツール紹介, kintone, 業務改善

ITが専門の中小企業診断士・ITコンサルタントとして、中小企業の業務改善・効率化をサポートしています。
その中でも面白いトピックとなる支援事例について、紹介していきたいと思います。

この記事は、こんな疑問や悩みを解決するヒントが欲しい方に適しています。

  • kintoneを使った業務効率化の事例を知りたい
  • kintoneの予定をカレンダーに連携させたい

スポンサーリンク

中小企業とkintoneは社長にやる気があれば相性が良い

中小企業のIT化サポートの事例として、何社かkintoneの導入を実施しました。
その結果、導入がうまくいくケースといかないケースの両方がありました。

今回の事例はうまくいったケースです。
ここの社長は、自ら見様見真似でJavaScriptのコードまで自分で書いてみた、という強者です。
(エラーが修正できずうまくはいかなかったのですが)

ですが、結果的にはこれぐらい経営者が意欲的に取り組むことが、システム導入をうまく運用させるポイントだと実感しました。

本題の、kintoneとGoogleカレンダーの連携について

今回は、一から自分でJavaScriptを組む作戦で対応します。
そんなに苦労しないだろう、という思い込みもありました…。

Googleカレンダー連携させるkintoneアプリを用意する

今回は、kintoneのカレンダービューが用意されている、サンプルアプリの「イベント・フェアカレンダー」をベースに例示します。サンプルアプリは、アプリ追加時の「おすすめアプリ」にあります。

おすすめのアプリの一覧。右下のイベント・フェアカレンダーを例に紹介

javascriptのプログラミングは、手頃に編集ができるkintoneのJS編集プラグインを利用しています。
こちらのページにプラグインが公開されてますので、おすすめです。

Googleカレンダー連携のためのAPIキーとカレンダーIDを取得する

Google APIを通じてアカウントへのアクセスが必要になるため、事前にGoogle側で設定を行っておく必要があります。GoogleカレンダーAPIの認証情報を設定するため、Google APIのデベロッパーコンソールにアクセスします。

動作確認でAPIが呼ばれてます

キーの取得とカレンダーIDの設定方法は、めんどくさくなったので割愛します。
こちらのページに記載の内容は、Google側の画像が若干古いのですが参考になると思います。

kintone の JavaScript プログラミング

kintone のJSプログラミングは、今回は試作的な部分もあることでGruntのようなビルドパッケージは利用せず、サイボウズが提供するプラグインを利用して組み込みました。プラグインはこちらで紹介しています。

Googleカレンダー連携に必要となるライブラリの組み込み

早速、Googleカレンダー連携を組み込んでいきます。
まずは、搭載するにあたって必要となるライブラリを組み込んでいきます。

プラグインの機能でライブラリを追加することができます

jsEditの機能で、チェックをすることで一般的によく使われるライブラリを追加することができます。
今回は、jQuery と Moment.js をチェックしましょう。

ただし、残念ながらGoogle API を利用するためのライブラリはこちらの選択肢にはありません。
ですので、アプリの環境設定からJavaScript / CSS でカスタマイズを選択し、追加します。

上の図にあるように、以下のライブラリを追加しました。
・https://apis.google.com/js/api.js
・spectrum.js (これは、後述の予定の色分けに関連)
・calendartest.js (これは、今回実装するJSファイルです)

Googleカレンダー連携のJS実装

JS側も実装していきます。コードのフローは、ざっくりと以下のようになります。

  1. Google API を初期化する(今回は、レコード編集画面表示時の処理で実施)
  2. レコード編集画面上にボタンを配置し、ボタンイベントでレコード内の予定をGoogleカレンダーに追加
  3. 予定の追加に成功したら、イベントIDを保持するためレコード更新
(function() {
  'use strict';
  var CLIENT_ID = '{{Google API のクライアントIDの値を設定}}';
  var API_KEY = '{{個々に発行したAPI Key の値を設定}}';
  var CALENDAR_ID = '{{カレンダーIDを設定}}';

  var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"];
  var SCOPES = "https://www.googleapis.com/auth/calendar.events";

  function initClient() {
    gapi.client.init({
      'apiKey': API_KEY,
      'discoveryDocs': DISCOVERY_DOCS,
      'clientId': CLIENT_ID,
      'scope': SCOPES
    }).then(function() {
      // Google認証済みのチェック
      if (!gapi.auth2.getAuthInstance().isSignedIn.get()) {
        // Google認証の呼び出し
        gapi.auth2.getAuthInstance().signIn();
      }
    });
  }
        
  function publishEvent(event) {
    // レコードのデータの取得
    var record = kintone.app.record.get().record;
    if (record) {
      // Google認証済みのチェック
      if (!gapi.auth2.getAuthInstance().isSignedIn.get()) {
        // Google認証の呼び出し
        gapi.auth2.getAuthInstance().signIn();
        alert('Google認証されていません。');
        return;
      }
      
      // 今回の例では予定を1時間の固定で登録します
      var end = new moment(record.start_datetime.value);
      end.add(1,'h');
      
      var params = {
        // イベントのタイトル
        'summary': record.event_name.value,         // フォームの「event_name」を参照
        'start': {
        // 終日の予定の場合はdate
        //'date' : end.format("YYYY-MM-DD"),
          'dateTime': record.start_datetime.value,  // フォームの「start_datetime」を参照
          'timeZone': 'America/Los_Angeles'
        },
        'end': {
        // 終日の予定の場合はdate
        //'date' : end.format("YYYY-MM-DD"),
          'dateTime': end.format(end._f),
          'timeZone': 'America/Los_Angeles'
        },
        'location': record.event_location.value,  // フォームの「event_location」を参照
        'description': record.event_description.value // フォームの「event_description」を参照
      };
      
      var request;
      // 既に登録済みのイベントの変更の場合
      if (record.event_id.value) {
        request = gapi.client.calendar.events.update(
          {
            'calendarId': CALENDAR_ID,
            'eventId': record.event_id.value,
            'resource': params
          }
        );
      } else {
        // 未公開のイベントの場合
        request = gapi.client.calendar.events.insert(
          {
            'calendarId': CALENDAR_ID,
            'resource': params
          }
        );
      }
      
      // Googleカレンダーへのイベント登録の実行
      request.execute(function(resp) {
        if (resp.error) {
          alert('イベントの登録に失敗しました。');
        } else {
          var body = {
            'app': kintone.app.getId(),
            'id': record.$id.value,
            'record': {
              'event_id': {
                'value': resp.result.id
              }
            }
          };
          return kintone.api(kintone.api.url('/k/v1/record', true), 'PUT', body).then(function(success) {
            alert('カレンダーにイベントを登録しました。');
            location.reload();
          }).catch(function(error) {
            alert('Google イベントIDの登録に失敗しました。');
          });
        }
      }, function(error) {
        alert('Google イベントIDの登録に失敗しました。');
      });
    }
  }
  
  kintone.events.on(['app.record.edit.show', 'mobile.app.record.edit.show'], function(event){
    event.record.event_id.disabled = true;
    return event;
  })

  kintone.events.on(['app.record.detail.show', 'mobile.app.record.detail.show'], function(event) {
    if (document.getElementById('publish_button') !== null) {
        return event;
    }

    // レコード詳細画面が表示されたら初期化の処理を行う
    gapi.load('client:auth2', {
      callback: function() {
        initClient();
      },
      onerror: function() {
        alert('gapi.client のロードに失敗しました!');
      }
    });
    
    // 画面下部のボタン
    var publishButton = document.createElement('button');
    publishButton.id = 'publish_button';
    publishButton.innerHTML = '公開する';
    publishButton.className = 'button-simple-cybozu geo-search-btn';
    publishButton.style = 'margin-top: 30px; margin-left: 10px;';
    publishButton.addEventListener('click', function() {
        publishEvent(event);
    });
    kintone.app.record.getSpaceElement('publish_button_space').appendChild(publishButton);

    return event;
  });
})();

このコードの詳細の説明は割愛します。
まあ、難しいことは書いてないと思うので、読んでみてください。

Googleカレンダー連携のフォーム実装

合わせて、フォームにボタンを登録するスペースを追加しておきます。
設定する要素IDはコードから読み取ってください(雑)。

こんな感じで、フォームにボタンとテキストが追加されればOKです。

(おまけ)次回記事で、Googleカレンダーの予定の色分けについて紹介するよ

今回、クライアントの要望でカレンダーの予定の色を変更できるようにしてほしいというのがありました。
こちらは、別のライブラリを用いて実現しましたので、別の記事で紹介したいと思います。


kintoneとGoogleカレンダー連携のまとめ

Google API の理解と、Auth認証のところで思ったより苦戦しました。
ですが、kintoneのカスタマイズ能力はやはり強力ですね。
いくらでも可能性があるので、エンジニアがいる会社ならとても適性があるサービスではないでしょうか。

もちろん、iPaaSの今後次第では、システム連携自体に悩む必要が無くなるかもしれませんが…それはまた別の話ですね。

スポンサーリンク