Garoon カスタマイズで外部システムの API を実行する〜打合せコスト算出カスタマイズ〜

目次

はじめに

Garoon スケジュール・ポータルのカスタマイズをはじめよう〜会議効率化カスタマイズ〜では、Garoon JavaScript APIとGaroon REST APIの使い方を知り、Garoonカスタマイズの基本を学びました。
この記事では、Garoon JavaScriptカスタマイズで外部システムのAPIを実行する方法を学んでいきましょう。

Garoonと社内で利用している他のシステムを連携させると、Garoonのポータル上に他のシステムの情報を出力したり、Garoonのワークフローを承認したら自動で外部システムにそのデータを送信したりできます。

この記事の内容は、「Cybozu Days 2019のGaroon中級ハンズオン〜APIを使って作る、カスタマイズポータル作成ハンズオン〜」で紹介したものです。

この記事で学べること

  • Garoon JavaScript APIを使って、外部システムのAPIを実行する方法

環境準備

  • Garoonクラウド版
  • テキストエディター

注意事項

次の画面ではカスタマイズを利用できません。

  • モバイル表示
  • モバイル用アプリ(KUNAIおよびGaroonモバイル)

ハンズオン

完成イメージ

参加者ごとに設定された単価から算出された予定の打合せコストを、予定の詳細画面で見える化します。
算出対象は「予定メニュー」が「打合」の予定です。

算出コストの内訳は、ブラウザーの開発者コンソールに出力されます。

参加者の単価は、ユーザー情報のカスタマイズ項目に設定します。
設定していないユーザーの単価は、0円で算出されます。

カスタマイズの適用範囲を人事部などのユーザーに限定して設定すると、適用されていないユーザーに打合せコストが表示されません。
開発者コンソールの算出コストの明細も同様です。
また、ユーザー情報のカスタマイズ項目も非公開やユーザーによる変更を不可に設定すると、cybozu.com管理者以外のユーザーには、プロフィール画面に単価は表示されません。

Step 1: ユーザー項目の追加

この設定には、cybozu.com共通管理者の権限が必要です。
手順の詳細は「 ユーザー情報の項目を追加する(カスタマイズ項目) (External link) 」を参照してください。

  1. Garoonメニュー右の歯車アイコンをクリックし、[cybozu.com共通管理]を選択します。
  2. 「ユーザー管理」の[組織/ユーザー]を選択します。
  3. [プロフィール項目の設定]をクリックします。
  4. 「カスタマイズ項目の設定」の[追加]ボタンをクリックします。
  5. 以下の内容を入力します。入力が終わったら、[保存]ボタンをクリックします。
    項目 設定する値
    項目名 任意の値を入力します。
    この記事では「単価」とします。
    項目コード 「UnitPrice」を入力します。
    タイプ 「文字列(1行)」を選択します。
    公開/非公開 「公開する」のチェックを外します。
    ユーザーによる変更 「ユーザーによる変更を許可する」のチェックを外します。

Step 2: プロキシ API の設定

  1. Garoonメニュー右の歯車アイコンをクリックし、[Garoonシステム管理]を選択します。
  2. [基本システムの管理]タブを選択し、[API]を選択します。
  3. [プロキシAPIの設定]を選択します。
  4. [追加する]をクリックします。
  5. 以下の内容を入力します。入力が終わったら、[追加する]ボタンをクリックします。

    項目 設定する値
    ステータス 「有効」を選択します。
    プロキシコード 「CalculatingCosts」を入力します。
    メソッド 「GET」を選択します。
    URL https://{subdomain}.cybozu.com
    {subdomain} は利用している Garoon 環境のサブドメインに置き換えてください。
    ヘッダー 次のキーと値の組み合わせを設定します。

Step 3: カスタマイズファイルの作成

  1. テキストエディタを開いて、次のコードの内容をコピー&ペーストします。

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    
    /**
     * Garoon meeting efficiency customize(portlet)
     * Copyright (c) 2020 Cybozu
     *
     * Licensed under the MIT License
     * https://opensource.org/license/mit/
     */
    
    (function($) {
      'use strict';
    
      // 「時間単価」が登録されている、ユーザー情報のカスタマイズ項目名
      const userCustom = 'UnitPrice';
      // プロキシコード
      const proxyCode = 'CalculatingCosts';
      // コスト算出対象の予定メニュー
      const MEETING = '打合';
    
      /**
       * 全参加者の「時間単価」(カスタマイズ項目)に数値が入っているかを確認する関数
       * @param {array} users 打合参加者
       * @returns {boolean} true:全て数値 false:1つでも数値以外の値が入っている
       */
      const isCheckedUnitPrice = function(users) {
        return users.every((user) => {
          const customItems = user.customItemValues;
          if (!customItems.length) {
            return true;
          }
          const unitPrice = customItems.filter((item) => {
            return item.code === userCustom;
          })[0];
          if (!unitPrice.value || unitPrice.value === '0') {
            return true;
          }
          return Number(parseInt(unitPrice.value, 10));
        });
      };
    
      /**
       * 打合コストを求める関数
       * @param {array} users 打合参加者
       * @param {number} meetingTime 打合所要時間
       * @returns {number} 打合コスト(人件費)
       */
      const calculateLaborCost = function(users, meetingTime) {
        const cost = users.reduce((price, user) => {
          const customItems = user.customItemValues;
          const unitPrice = customItems.filter((item) => {
            return item.code === userCustom;
          })[0];
          if (!unitPrice) {
            return price;
          }
          return price + Number(unitPrice.value);
        }, 0);
    
        const total = Math.round(cost * parseFloat(meetingTime));
        // 会議のコストを表示
        console.log('打合コストの内訳: ' + cost.toLocaleString() + ' 円 * ' + meetingTime.toLocaleString() + '時間 = ' + total.toLocaleString() + ' 円');
        return total;
      };
    
      garoon.events.on('schedule.event.detail.show', (event) => {
        const scheduleEvent = event.event;
        let query = '';
        const start = scheduleEvent.start.dateTime;
        const end = scheduleEvent.end.dateTime;
        const attendees = scheduleEvent.attendees;
        const startTime = new Date(start);
        const endTime = new Date(end);
        const meetingTime = (endTime - startTime) / (1000 * 60 * 60);
        const path = 'https://' + location.hostname + '/v1/users.json?';
    
        // 予定メニューが「打合」以外、もしくは参加者が0名の場合、処理を終了する
        if (scheduleEvent.eventMenu !== MEETING || attendees < 1) {
          return;
        }
    
        // code(ログイン名)を利用したクエリ生成
        query = attendees.map((attendee, index) => {
          return 'codes[' + index + ']=' + attendee.code;
        }).join('&');
    
        garoon.base.proxy.send(proxyCode, path + query, 'GET', {}, {}).then((resp) => {
          const users = JSON.parse(resp[0]).users;
    
          if (!isCheckedUnitPrice(users)) {
            window.alert('数値ではない値が時間単価に設定されている可能性があります');
            return;
          }
    
          const total = calculateLaborCost(users, meetingTime);
          const headerSpace = garoon.schedule.event.getHeaderSpaceElement();
          const $span = $('<STRONG>', {
            id: 'items',
            class: 'sat_color1_grn_kit',
            text: '※本打合コスト: ' + total.toLocaleString() + ' 円'
          });
          $span.prependTo(headerSpace);
        }).catch((e) => {
          window.alert('コスト表示に失敗しました。原因については、システム管理者にご相談ください。');
          console.log('※エラー詳細 \n ', e);
        });
      });
    })(jQuery.noConflict(true));
  2. ファイルの拡張子「.js」、文字コードは「UTF-8(BOMなし)」で、ファイルに名前を付けて保存します。
    この記事では、ファイル名をcalculating-costs.jsとしています。

Step 4: カスタマイズの適用

カスタマイズグループを作成し、スケジュールのカスタマイズファイルを適用します。
手順の詳細は「 スケジュールのカスタマイズ設定 (External link) 」を参照してください。

  1. Garoonメニュー右の歯車アイコンをクリックし、[Garoonシステム管理]を選択します。
  2. [各アプリケーションの管理]タブを選択し、[スケジュール]を選択します。
  3. [JavaScript/CSSによるカスタマイズ]を選択します。
  4. [カスタマイズグループを追加する]をクリックします。
  5. 次の内容を入力します。入力が終わったら、[追加する]ボタンをクリックします。
    項目 設定する値
    カスタマイズ 「適用する」を選択します。
    カスタマイズグループ名 任意の値を入力します。この記事では「会議効率化カスタマイズ」としています。
    適用対象 管理部など、打合せのコストを表示しても問題ないユーザーや部署を選択してください。
    JavaScript カスタマイズ 以下の順で、URL およびファイルを指定します。
    1. https://js.cybozu.com/jquery/3.1.1/jquery.min.js
    2. calculating-costs.js(カスタマイズファイル)
    CSS カスタマイズ grn_kit.css を指定します
    grn_kit.css(Garoon html/css/image-Kit for Customize)の入手方法

grn_kit.css(Garoon html/css/image-Kit for Customize)の入手方法

  1. https://github.com/garoon/css-for-customize (External link) にアクセスします。
  2. [Clone or download]ボタンをクリックして、「Download ZIP」を選択します。
  3. ダウンロードしたzipファイルを解凍します。
  4. 解凍したファイルの「css」フォルダー以下の「grn_kit.css」を利用します。

コードの解説

プロキシ API 設定

プロキシAPI設定は、ブラウザーから外部システムのAPIを実行するためのGaroonのしくみです。
garoon.base.proxy.send()を実行すると、あらかじめ設定しておいたプロキシ設定を利用して、Garoonサーバーから外部システムのAPIへのリクエストを送信します。
参考: プロキシ API の設定 (External link)

また、外部システムのAPIの実行に必要な認証情報を、プロキシAPI設定で設定しカスタマイズファイル内にハードコーディングしないので、セキュアにAPIを実行できます。
参考: セキュアコーディングガイドライン - 認証情報や認可情報を適切に取り扱う
User APIの場合は、「X-Cybozu-Authorization」というリクエストヘッダーが認証情報です。

tips
補足

今回のサンプルでは、cybozu.comのUser APIを外部システムのAPIに見立て実行していますが、GaroonカスタマイズでUser APIを実行する場合には、プロキシAPIは設定不要でAPIを実行できます。
例: User API のユーザー情報を使って Garoon ワークフローをカスタマイズする

garoon.base.proxy.send()

garoon.base.proxy.send()は、Garoonから外部システムのAPIを実行するGaroon JavaScript APIです。
今回のコードでは、次の箇所で利用しています。

 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
    garoon.base.proxy.send(proxyCode, path + query, 'GET', {}, {}).then((resp) => {
      const users = JSON.parse(resp[0]).users;

      if (!isCheckedUnitPrice(users)) {
        window.alert('数値ではない値が時間単価に設定されている可能性があります');
        return;
      }

      const total = calculateLaborCost(users, meetingTime);
      const headerSpace = garoon.schedule.event.getHeaderSpaceElement();
      const $span = $('<STRONG>', {
        id: 'items',
        class: 'sat_color1_grn_kit',
        text: '※本打合コスト: ' + total.toLocaleString() + ' 円'
      });
      $span.prependTo(headerSpace);
    }).catch((e) => {
      window.alert('コスト表示に失敗しました。原因については、システム管理者にご相談ください。');
      console.log('※エラー詳細 \n ', e);
    });

今回実行する ユーザー情報をエクスポートする APIに合わせて、garoon.base.proxy.send(プロキシコード, URL, メソッド, APIに渡すデータ) という形で指定します。
このとき、Promiseを使ってAPIを実行しています。
Promiseについては、 Promise と async/await kintone カスタマイズで非同期処理をするを参照してください。

  • プロキシコード:「プロキシAPI設定」で設定したプロキシコード

  • URL:APIユーザーエクスポートAPI(JSON)のURLを指定します。検索条件(今回は予定の参加者のユーザーコード)をURLパラメーターで指定しています。
    今回のコードでは、変数pathqueryを結合した値を指定していますが、pathqueryは次の部分で定義しています。

    73
    
        const path = 'https://' + location.hostname + '/v1/users.json?';
    80
    81
    82
    83
    
        // code(ログイン名)を利用したクエリ生成
        query = attendees.map((attendee, index) => {
          return 'codes[' + index + ']=' + attendee.code;
        }).join('&');

    たとえば、予定の参加者のユーザーコードがyamadaとsatoの場合、次のURLが指定されます。
    https://{subdomain}.cybozu.com/v1/users.json?codes[0]=yamada&codes[1]=sato

  • メソッド:GET

  • API に渡すデータ:ありません。

正常に実行できたら.then()の引数で指定した関数の処理を実行し、エラーが発生したら.catch()の引数で指定した関数の処理を実行します。
今回、正常に実行できたときは、取得したUnitPriceの値を使って打合せコストを算出し、画面上に表示しています。

動作確認

  1. ユーザー情報を変更し、「UnitPrice」に単価を設定します。
    参考: ユーザー情報の変更 (External link)
  2. 予定を登録します。
    予定メニュー:「打合」を選択します
    参加者:1. で単価を設定したユーザーを選択します。
  3. 登録した予定の詳細画面を開きます。タイトルの下部に打合せコストが表示されていることを確認します。

おわりに

cybozu developer networkでは、さまざまな Garoon の連携カスタマイズ Tipsを公開しています。
Tipsに掲載しているコードで「どんなことをしているか?」を確認しながら、ぜひカスタマイズに挑戦してみてください。

利用している Garoon API

利用しているライブラリ

  • jQuery v3.1.1, ドキュメント(外部サイト) (External link)
    HTML要素の生成や操作を楽に扱うことができるライブラリです。
    打合せコストを表示する <>HTML要素の作成や予定の詳細画面への挿入などに利用しています。
  • Garoon html/css/image-Kit for Customize, ドキュメント
    ボタンなどのUIパーツをGaroonの見た目に調和させるスタイルシートのライブラリです。
    打合せコストを表示するHTML要素の背景色に利用しています。
information

このTipsは、2020年6月版Garoonで動作を確認しています。