Kintoneで他のアプリからユーザー選択の値を取得する

やりたいこと

とあるアプリで、他のアプリから値を取得して任意のフィールドに設定したいのです。

  1. [社員名簿]アプリでは、「社員名」と「承認者」を紐づけている。
  2. [作業依頼申請]アプリでは、「申請者」とその「承認者」を入れる必要がある。

上記のような状態のアプリがあったとします。 [作業依頼申請]アプリで「承認者」を入力するのはとても面倒くさいので、[社員名簿]アプリから「承認者」を取得して設定したいです。 f:id:ponsuke_tarou:20210622213458j:plain

[社員名簿]アプリ

社員名簿 - kintone(キントーン)- すぐに使えるサンプルアプリ | サイボウズの業務改善プラットフォームを使っています。

f:id:ponsuke_tarou:20210622211206p:plain
[社員名]フィールドというユーザー選択フィールドを追加しています。
今回使うフィールドは以下です。

フィールド名 フィールドコード フィールドの種類
社員名 社員名 ユーザー選択
承認者 承認者 ユーザー選択

[作業依頼申請]アプリ

作業依頼申請 - kintone(キントーン)- すぐに使えるサンプルアプリ | サイボウズの業務改善プラットフォームを使っています。 f:id:ponsuke_tarou:20210622212125p:plain 今回使うフィールドは以下です。

フィールド名 フィールドコード フィールドの種類
申請者 applicant 作成者
承認者 authorizer ユーザー選択

JavaScriptを実装します。

レコード編集を始めるタイミングで処理を起動します。

kintone.events.on([
    'app.record.index.edit.show',
    'app.record.create.show',
    'app.record.edit.show'
], function(event) {

レコード編集を始めるタイミングで処理が動くようにイベントハンドラーを登録します。

イベントハンドラーを登録する

kintone.events.on(type, handler)

イベント処理の記述方法 – cybozu developer network

引数のtypeには以下のタイミングを指定します。

イベントが発生するタイミング イベントタイプ Promise対応
レコード一覧画面 - インライン編集開始 app.record.index.edit.show o
レコード追加画面 - 表示後 app.record.create.show o
レコード編集画面 - 表示後 app.record.edit.show o

表の参考元 : kintone JavaScript API(イベント)一覧 – cybozu developer network

APIの呼び出しを行うので、kintone.Promiseに対応したイベントを使います。

※ この kintone.Promise オブジェクトを return することで、kintone.api() の実行を待ってから次の処理を実行できるイベントがあります。 対応しているイベントはこちらをご参照ください。

kintone REST API リクエスト – cybozu developer network

他のアプリでの検索条件に使う値を取得する。

[社員名簿]アプリから情報を取得する検索条件に使う値を取得します。 処理を「新規作成」「新規作成以外(既に登録されているレコードの編集)」に分岐させています。

if (event.type === 'app.record.create.show') {
    // レコード追加時には、[申請者]フィールドにログインユーザーが設定されていないので、ログインユーザー情報を直接取得する
    user = kintone.getLoginUser().code;
} else {
    // [申請者]フィールドからユーザー情報を取得する
    user = event.record['applicant']['value']['code'];
}

検索条件に使用する[社員名簿]アプリの[申請者]フィールドには[作成者]を使用しています。

https://jp.cybozu.help/k/img-ja/created_by_01.png

レコードを保存したユーザーが自動で設定されるフィールドです。

作成者 - kintone ヘルプ

この[作成者]は、新規作成して初回保存時に設定されるようです。 ということで、「新規作成」の時はログインユーザーから情報を取得するようにしています。

Kintone REST APIで他のアプリからユーザー選択の値を取得する

APIを使って[社員名簿]アプリの[承認者]フィールドを取得します。検索条件は「[社員名簿]アプリの[社員名]フィールドが、[作業依頼申請]アプリの[申請者]フィールドと同じ」です。

let params = {
    app: 11,
    fields: ['承認者'],
    query: '社員名 in ("' + user + '")'
};

表の出典 : レコードの一括取得(クエリで条件を指定) – cybozu developer network

パラメータ名 指定する値 必須 説明
fields 文字列の配列 省略可 レスポンスに含めるフィールドコードを指定します。
省略した場合は、閲覧権限を持つすべてのフィールドの値が返されます。
app 数値又は文字列 必須 アプリのID を指定します。
query 文字列 省略可 レスポンスに含めるレコードの条件を指定するクエリ文字列です。 クエリ文字列内では、後述の演算子とオプションが使用できます。
省略した場合は、閲覧権限を持つすべてのレコードが返されます。

Kintone REST APIの呼び出しで、コールバック関数を指定する方法と省略する方法があります。 ここでは、コールバック関数を省略してkintone.Promise オブジェクトが返却されるようにします。

Promiseを使う利点

レコード作成時などに、処理を待ってからレコードを保存することができる(同期的処理、と呼びます) 「あるアプリAのレコードを保存時、アプリBのレコードを取得し、その値を利用」というようにレコードの保存時などにkintone APIを使って他のデータを取得したり変更したり、同期的に処理することができるようになります。

kintoneにおけるPromiseの書き方の基本 – cybozu developer network

そうすることで、APIを使って[社員名簿]アプリから取得したデータを画面のフィールドに反映させることができます。

return kintone.api(kintone.api.url('/k/v1/records.json', true), 'GET', params).then( function(resp) {
    // API の呼び出しが成功したら実行される処理...
    return event;
}).catch(function(error) {
    // API の呼び出しが失敗したら実行される処理...
    return event;
});

f:id:ponsuke_tarou:20210622214925j:plain

ソースファイルを登録すれば出来上がりです。

  1. [作業依頼申請]アプリの設定画面を開く
    • f:id:ponsuke_tarou:20210630184357p:plain
  2. f:id:ponsuke_tarou:20210630184514p:plain
    [設定]タブ > [JavaScript / CSSでカスタマイズ]リンクからJavaScriptを登録する画面を表示
  3. [適用範囲]=「すべてのユーザーに適用」を選択
  4. [PC用のJavaScriptファイル]の[アップロードして追加]ボタンで実装したソースファイルをアップロード
  5. f:id:ponsuke_tarou:20210630184802p:plain
    [保存]ボタンで保存
  6. f:id:ponsuke_tarou:20210630184935p:plain
    [アプリを更新]ボタンで適用する

できた!

f:id:ponsuke_tarou:20210630185439p:plain
[社員名簿]アプリにこんなレコードがあると・・・
こんな感じで、[作業依頼申請]アプリでレコードを追加したり編集すると[社員名簿]アプリから「承認者」を取得して自動で設定されるようになりました。 f:id:ponsuke_tarou:20210630185649g:plain

うまくいかなかったこと

APIで取得した結果を設定したのに反映されない!

原因 : ハンドラーが return してないから

return kintone.api(kintone.api.url('/k/v1/records.json', true), 'GET', params).then(function(resp) {
    // ...省略...
    event.record['authorizer']['value'] = userlist;
}).catch(function(error) {
    console.log(error.message);
});

フィールドの値を書き換える

ハンドラーが record オブジェクトのフィールドの値を書き換えて event オブジェクトを return した場合、その値でフィールドの値を更新します。

  • 最後のハンドラーが return しない場合、フィールドの値を更新しません。

レコード編集イベント – cybozu developer network

原因 : Promiseオブジェクトを使う方法じゃないから

return kintone.api(kintone.api.url('/k/v1/records.json', true), 'GET', params, function(resp) {
    // API の呼び出しが成功したら実行されるコールバック処理
    return event;
}, function(error) {
    // API の呼び出しが失敗したら実行されるコールバック処理
    return event;
});

コールバック関数を指定しており、kintone.Promise オブジェクトは返却されません。 というわけでなのでAPIを呼び出す処理が終わるのを待たずに画面表示処理が進んでしまいました。

このように kintone.api() はコールバック関数を省略するとPromiseオブジェクトが返り値になります。

kintone.api(kintone.api.url('/k/v1/record', true), 'GET', {app: 1, id: 1}); // これでPromiseオブジェクトが生成される

それを return してあげることによってkintone側で app.record.create.submit 時など、処理を待ってくれる仕組みをkintoneは持っています。Promiseオブジェクトを return しないと処理をまってくれないので注意しましょう。(逆に言えば、レコード詳細ページなど、処理を待たせる必要がなければPromiseオブジェクトの return は必須ではありません。)

kintoneにおけるPromiseの書き方の基本 – cybozu developer network

Failed to load resource: the server responded with a status of 400 (Bad Request) - 入力内容が正しくありません。

f:id:ponsuke_tarou:20210622185513p:plain
ブラウザのコンソールにエラーが出力された

  • 事象 : APIの呼び出しでエラー
  • 原因 : APIでの検索条件で文字列が"に囲まれていないから
  • 対応 : "で囲む
/** @type {object} API に渡すパラメータオブジェクト. */
let params = {
    app: 11,
    fields: ['承認者'],
    query: '社員名 in (' + user + ')' // 正解>>> '社員名 in ("' + user + '")'
};

520 (520) - 社員名フィールドのフィールドタイプには演算子=を使用できません。

  • 事象 : APIの呼び出しでエラー
  • 原因 : APIでの検索条件でユーザー選択フィールドに=演算子を使ったから
  • 対応 : ユーザー選択には、innot inを使う
/** @type {object} API に渡すパラメータオブジェクト. */
let params = {
    app: 11,
    fields: ['承認者'],
    query: '社員名 = "' + user + '"' // 正解>>> '社員名 in ("' + user + '")'
};
フィールド又はシステム識別子 利用可能な演算子 利用可能な関数
ユーザー選択 in not in LOGINUSER()

表の出典元 : フィールド、システム識別子ごとの利用可能な演算子と関数一覧 – cybozu developer network

f:id:ponsuke_tarou:20210622214554j:plain
PARADIS小石川本店のケーキ