やりたいこと
スケジュールをOutlookカレンダーで行っているので、そのデータを元に工数を集計できるようにしたい。
仕様
一覧画面に集計開始日と終了日を日付形式で設定し、その期間のOutlookカレンダーのデータを取得して一覧にする。
既に登録済みのデータは更新する。
ソースはこちら
公式にサンプルがあるのでこれをベースにする
Outlook連携 - kintoneからOutlookスケジュールを登録しよう!!
公式サンプルでは予定の登録だけなので、これを改造して予定を取得をできるようにする
JS/CSS
サンプルからの変更としては dateformat.js
を追加しています。
- JS
- https://code.jquery.com/jquery-3.5.1.min.js
- dateformat.js
- https://secure.aadcdn.microsoftonline-p.com/lib/1.0.0/js/msal.js
- kintone-ui-comopnent.min.js
- kintone-js-sdk.min.js
- https://js.cybozu.com/sweetalert2/v8.17.6/sweetalert2.min.js
- common-js-functions.min.js
- kintone-connect-outlook-schedule-common.js
- oauth.js
- kintone-connect-outlook-schedule.js
- https://kintone.github.io/kintoneUtility/kintoneUtility.min.js
- CSS
- kintone-ui-component.min.css
- https://js.cybozu.com/sweetalert2/v8.17.6/sweetalert2.min.css
フォーム設定
項目 | フィールド形式 | フィールドコード |
---|---|---|
イベントID | 文字列(1行) | EventId |
更新キー | 文字列(1行) | UpdateKey |
件名 | 文字列(1行) | To_Do |
担当者 | ユーザー選択 | Assignees |
開始日 | 日時 | From |
終了日 | 日時 | To |
工数(時) | 数値 | WorkTime |
終日 | チェックボックス | AllDay |
詳細内容 | 文字列(複数行) | Details |
添付ファイル | 添付ファイル | Attachments |
ヘッダー部をカスタマイズ
一覧画面のヘッダー部に「取得開始日」、「取得終了日」、「実行ボタン」配置の設定をする
言語情報にボタン表記名を追加
kintone-connect-outlook-schedule.js
lang: (
en: {
getEvent: 'Get Event',
},
ja: {
getEvent: '予定を取得',
}
settings: (
ui: {
buttons: {
getEvent: {
text: 'getEvent',
type: 'normal'
}
}
}
}
日付項目とボタンのスタイルを設定
kintone-connect-outlook-schedule.js
uiCreateForIndex: function(kintoneHeaderSpace) {
~
// ラベルを設定
this.data.ui.btnGetEvent = this.createButton(this.setting.ui.buttons.getEvent, this.setting.i18n.button);
this.data.ui.labelFromDate = new kintoneUIComponent.Label({text: '取得開始日'});
this.data.ui.labelToDate = new kintoneUIComponent.Label({text: '取得終了日'});
// スタイルを設定
this.data.ui.btnGetEvent.element.style.display = 'inline-block';
this.data.ui.btnGetEvent.element.style.margin = '0 15px 15px 15px';
this.data.ui.labelFromDate.element.style.display = 'inline-block';
this.data.ui.labelFromDate.element.style.margin = '0 15px 15px 15px';
this.data.ui.labelToDate.element.style.display = 'inline-block';
this.data.ui.labelToDate.element.style.margin = '0 15px 15px 15px';
// 日付の初期値を入力
var iFrom = storage.inputFromDate ? new Date(storage.inputFromDate) : new Date();
var iTo = storage.inputToDate ? new Date(storage.inputToDate) : new Date();
this.data.ui.inputFromDate = new kintoneUIComponent.DateTime({value: iFrom, type: 'date', locale: 'ja', dateFormat: 'YYYY/MM/dd'});
this.data.ui.inputToDate = new kintoneUIComponent.DateTime({value: iTo, type: 'date', locale: 'ja', dateFormat: 'YYYY/MM/dd'});
// 各要素を配置
this.data.ui.kintoneCustomizeOutlookHeaderSigned.appendChild(this.data.ui.labelFromDate.render());
this.data.ui.kintoneCustomizeOutlookHeaderSigned.appendChild(this.data.ui.inputFromDate.render());
this.data.ui.kintoneCustomizeOutlookHeaderSigned.appendChild(this.data.ui.labelToDate.render());
this.data.ui.kintoneCustomizeOutlookHeaderSigned.appendChild(this.data.ui.inputToDate.render());
this.data.ui.kintoneCustomizeOutlookHeaderSigned.appendChild(this.data.ui.btnGetEvent.render());
}
ボタン押下時のイベント登録する
kintone-connect-outlook-schedule.js
kintone.events.on('app.record.index.show', function(event) {
~
// 予定取得ボタン押下時
kintoneScheduleService.data.ui.btnGetEvent.on('click', function() {
outlookAPI.getEvent();
});
}
APIリクエストを設定する
calendarviewを使うので追加しましょう。
こんな感じで変更しました。
kintone-connect-outlook-schedule-common.js
graphApiScorp: {
scopes: [
'calendars.readwrite',
'calendars.read'
],
},
event: {
eventUrl: 'https://graph.microsoft.com/v1.0/me/events',
eventGetUrl: 'https://graph.microsoft.com/v1.0/me/calendarview'
},
kintone: {
fieldCode: {
// 件名
subject: 'To_Do',
// 詳細内容
body: 'Details',
// 開始日時
startDate: 'From',
// 終了日時
endDate: 'To',
// イベントID
eventId: 'EventId',
// 添付ファイル
attachFile: 'Attachments',
// 更新キー
updateKey: 'UpdateKey',
// 終日
isAllDay: 'AllDay',
// ユーザー指定
assignees: 'Assignees',
// 工数
worktime: 'WorkTime',
}
}
ボタン押下時の処理を追加する
宣言部で必要な要素を取得する
kintone-connect-outlook-schedule.js
var SUBJECT_FIELD_CODE = KAC.kintone.fieldCode.subject;
var BODY_FIELD_CODE = KAC.kintone.fieldCode.body;
var START_DATE_FIELD_CODE = KAC.kintone.fieldCode.startDate;
var END_DATE_FIELD_CODE = KAC.kintone.fieldCode.endDate;
var ATTACH_FILE_FIELD_CODE = KAC.kintone.fieldCode.attachFile;
var EVENT_ID_FIELD_CODE = KAC.kintone.fieldCode.eventId;
var EVENT_URL = KAC.event.eventUrl;
var UPDATE_KEY_FIELD_CODE = KAC.kintone.fieldCode.updateKey;
var ALLDAY_FIELD_CODE = KAC.kintone.fieldCode.isAllDay;
var ASSIGNEES_FIELD_CODE = KAC.kintone.fieldCode.assignees;
var WORKTIME_FIELD_CODE = KAC.kintone.fieldCode.worktime;
var EVENT_GET_URL = KAC.event.eventGetUrl;
予定表からデータを取得する
kintone-connect-outlook-schedule.js
var outlookAPI = {
~
// outlook予定表取得
getEvent: function() {
var self = this;
var accessToken;
var header;
KC.ui.loading.show();
// アクセストークンを取得
if (kintoneScheduleService.isExpireAccessToken()) {
accessToken = storage.getItem('SESSION_KEY_TO_ACCESS_TOKEN');
} else {
KC.ui.loading.hide();
return;
}
// タイムゾーンを日本設定
header = {
'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'application/json',
'Prefer': 'outlook.timezone=\"Tokyo Standard Time\"',
'outlook.body-content-type': 'text'
};
// ヘッダーの日付情報を取得してリクエストURLを作成
var dateFormat = new DateFormat("yyyy/MM/dd");
var fromDate = dateFormat.format(new Date(kintoneScheduleService.data.ui.inputFromDate.getValue()));
var toDate = dateFormat.format(new Date(kintoneScheduleService.data.ui.inputToDate.getValue()));
var eventGetUrl = EVENT_GET_URL + "?StartDatetime=" + fromDate + "T00:00:00" + "&EndDatetime=" + toDate + "T23:59:59";
// 入力内容を保持(再表示用)
storage.inputFromDate = kintoneScheduleService.data.ui.inputFromDate.getValue();
storage.inputToDate = kintoneScheduleService.data.ui.inputToDate.getValue();
// Outlookの予定表取得
kintone.proxy(eventGetUrl, 'GET', header, {}).then(function(res) {
var data = JSON.parse(res[0]).value;
if (data === undefined) {
Swal.fire({
title: 'ERROR!',
type: 'error',
text: kintoneScheduleService.setting.i18n.message.error.accessOutlookFailure,
allowOutsideClick: false
});
KC.ui.loading.hide();
return;
}
// データがない場合
if (data.length === 0) {
Swal.fire({
title: 'WARN!',
type: 'warning',
text: kintoneScheduleService.setting.i18n.message.warning.noMail,
allowOutsideClick: false
});
KC.ui.loading.hide();
return;
}
// 取得したスケジュールを登録
self.putScheduleToKintoneApp(0, data, accessToken).catch(function(err) {
Swal.fire({
title: 'ERROR!',
type: 'error',
text: kintoneScheduleService.setting.i18n.message.error.addKintoneRecordFailure,
allowOutsideClick: false
});
KC.ui.loading.hide();
});
}, function(err) {
Swal.fire({
title: 'ERROR!',
type: 'error',
text: kintoneScheduleService.setting.i18n.message.error.getOutlookMailFailure,
allowOutsideClick: false
});
KC.ui.loading.hide();
});
},
~
};
取得したデータのチェック
新規なら登録処理、登録済みなら更新処理に振り分ける
kintone-connect-outlook-schedule.js
var outlookAPI = {
~
// 取得した予定をkintoneへ登録
putScheduleToKintoneApp: function(index, data, accessToken) {
var self = this;
// キントーンに登録済みのデータかをチェックする
return this.getScheduleIDIndexIsNotRetrived(index, data).then(function(indexSchedule) {
// 登録済の予定なら更新
if (indexSchedule === null) {
return self.updateScheduleIntoKintone(data[index], accessToken).then(function(result) {
// 次のデータ
if (index + 1 < data.length) {
return self.putScheduleToKintoneApp(index + 1, data, accessToken);
}
return null;
});
}
// 未登録の予定なら登録
return self.addScheduleIntoKintone(data[indexSchedule], accessToken).then(function(result) {
// 次のデータ
if (indexSchedule + 1 < data.length) {
return self.putScheduleToKintoneApp(indexSchedule + 1, data, accessToken);
}
return null;
});
}).then(function(resp) {
window.location.reload();
KC.ui.loading.hide();
});
},
getScheduleIDIndexIsNotRetrived: function(index, data) {
var self = this;
// 取得済み予定かチェック
return self.checkSchedulesIsNotExistsOnkintone(data[index].id.substr(data[index].id.length-64)).then(function(resp) {
if (resp === false) {
if (data.length <= index + 1) {
return null;
}
return self.getScheduleIDIndexIsNotRetrived(index + 1, data);
}
return index;
});
},
// 取得済み予定かチェック
checkSchedulesIsNotExistsOnkintone: function(scheduleID) {
var dataRequestkintoneApp = {
app: kintone.app.getId(),
fields: ['$id'],
query: UPDATE_KEY_FIELD_CODE + ' like "' + scheduleID + '"',
totalCount: true
};
return kintoneSDKRecord.getRecords(dataRequestkintoneApp).then(function(response) {
if (!response.records || response.records.length === 0) {
return true;
}
return false;
});
},
// 登録処理呼び出し
addScheduleIntoKintone: function(data) {
var kintoneRecord = outlookAPI.setParam(data);
return outlookAPI.addKintone(kintoneRecord);
},
// 更新処理呼び出し
updateScheduleIntoKintone: function(data) {
var kintoneRecord = outlookAPI.setParam(data);
return outlookAPI.updateKintone(kintoneRecord, data.id);
},
~
};
取得したデータを設定する
kintone-connect-outlook-schedule.js
var outlookAPI = {
~
setParam: function(data) {
var kintoneRecord = {};
// Subject
kintoneRecord[SUBJECT_FIELD_CODE] = {
value: data.subject
};
// From
kintoneRecord[START_DATE_FIELD_CODE] = {
value: data.start.dateTime.substr(0, 19)
};
// To
kintoneRecord[END_DATE_FIELD_CODE] = {
value: data.end.dateTime.substr(0, 19)
};
// Details
kintoneRecord[BODY_FIELD_CODE] = {
value: data.body.content
};
// WorkTime (工数を算出する)
var sDate = new Date(data.start.dateTime.substr(0, 19));
var eDate = new Date(data.end.dateTime.substr(0, 19));
var sTime = sDate.getHours() * 60 + sDate.getMinutes();
var eTime = eDate.getHours() * 60 + eDate.getMinutes();
var workTime = (eTime - sTime) / 60;
kintoneRecord[WORKTIME_FIELD_CODE] = {
value: workTime
};
// Assignees(ログインユーザを固定で設定)
kintoneRecord[ASSIGNEES_FIELD_CODE] = {
value: [{code: storage.getItem('SIGN_USER_DISPINFO')}]
};
// eventId(OutlookのイベントID)
kintoneRecord[EVENT_ID_FIELD_CODE] = {
value: data.id
};
// updateKey(OutlookのイベントIDの末尾64桁)
kintoneRecord[UPDATE_KEY_FIELD_CODE] = {
value: data.id.substr(data.id.length-64)
};
// isAllDay
var allday = [];
if (data.isAllDay) {
allday.push("終日");
}
kintoneRecord[ALLDAY_FIELD_CODE] = {
value: allday
};
return kintoneRecord;
},
~
};
登録処理
kintone-connect-outlook-schedule.js
var outlookAPI = {
~
// kintoneへ登録
addKintone: function(postParam) {
var param = {
app: kintone.app.getId(),
record: postParam
};
return kintoneSDKRecord.addRecord(param);
},
~
};
更新処理
kintone-connect-outlook-schedule.js
var outlookAPI = {
~
// kintoneへ更新
updateKintone: function(putParam, eventId) {
// 更新キーはレコード情報から削除する
delete putParam[UPDATE_KEY_FIELD_CODE];
// 更新キーを設定
var updateKey = {
field: UPDATE_KEY_FIELD_CODE,
value: eventId.substr(eventId.length-64)
}
var param = {
app: kintone.app.getId(),
updateKey: updateKey,
record: putParam
};
return kintoneSDKRecord.updateRecordByUpdateKey(param);
},
~
};
サンプルの機能ではまだ微妙なのでさらに改修します
▼改修内容はこちら
- KintoneとOutlookカレンダーを連携して工数を集計する #2
トラブルシューティング
エラー
アクセストークンが期限切れと怒られる
→ログアウトしてもっかいログインする
{
"error": {
"code": "InvalidAuthenticationToken",
"message": "Access token has expired.",
"innerError": {
"request-id": "********-****-****-****-************",
"date": "2020-06-04T06:52:27"
}
}
}