CATEGORIES:JAVA KINTONE
kintoneで自アプリと他アプリの情報を種別に応じて取り込む

kintoneの編集画面にボタンを置き、押すと過去の情報をまとめて取り込む方法。解約申請、入替申請、廃止申請のように、以前の申請内容を引き継ぐ申請で使える。

このボタンでできることは次の3つ。

#できること何が便利か
1自アプリの過去レコードから取り込む以前のレコードを開いて項目ごとにコピペする手間がなくなる。最新のレコードで足りない項目は古いレコードで補う
2他アプリのレコードからも取り込む共通のキー(申請番号など)で別アプリの値を引っぱってくる。2つのアプリを行き来しなくてよい
3種別によって取り込む項目を切り替える契約種別や回線種別で入力するフィールドが変わるアプリでも、使うフィールドにだけ値を入れられる

kintone標準の「ルックアップ」でも他アプリから値をコピーできるが、キーを1つ選んで1件を取るだけ。「条件に合う複数件から空欄を補う」「種別で入れる項目を変える」といったことは、JavaScriptでないとできない。

今回は回線工事の廃止申請を例にする。使うアプリは次の2つで、申請番号でつながっている。

アプリ役割この例で使う項目
アプリA:回線工事申請アプリ(スクリプトを置く)新規(開設)か廃止かを選んで工事を依頼する回線種別、対象コード、回線番号、ステータス
アプリB:案件申請アプリ工事依頼を受け付ける工事日時

廃止申請を作るとき、開設時に登録した回線情報をそのまま使いたい。ただし、フレッツ回線とダークファイバーでは入力するフィールドが違い、アプリAも回線種別でフィールドの表示・非表示を切り替えている。先の3つがちょうど当てはまるケース。

APIは2回とも読み取りだけで、どちらのアプリにも書き込まない。取得した値は kintone.app.record.set() で画面に反映するだけで、保存はユーザーが行う。

1. 自アプリの過去レコードから取り込む

kintone.app.getId() で自アプリを指定し、共通の項目(この例ではビルコード)とステータスで絞り込む。アプリIDを直書きしないので、アプリを複製してもそのまま動く。

var query2 = 'ビルコード in ("' + buildingCode + '") and ラジオボタン_23 in ("利用中") order by 日付_1 desc';
var records = await kintone.api(kintone.api.url('/k/v1/records', true), 'GET', {
  app: kintone.app.getId(),
  query: query2
});

取得した中で最新のレコードを基準にコピーし、空欄は古いレコードから順に補う。変更や増設で申請が分かれ、最新レコードに一部の項目しかない場合に対応できる。
条件に合うレコードが0件のときは中断する。この例では「利用中」が0件=すでに廃止済みなので、二重の廃止申請を防ぐガードも兼ねている。

// 最新レコードからコピー
var baseRecord = records.records[0];
fieldsToCopy.forEach(function(field) {
  record[field].value = baseRecord[field].value;
});

// 空欄は2件目以降で補う
for (var i = 1; i < records.records.length; i++) {
  var nextRecord = records.records[i];
  fieldsToCopy.forEach(function(field) {
    if (!record[field].value || record[field].value === '') {
      record[field].value = nextRecord[field].value;
    }
  });
}

2. 他アプリのレコードからも取り込む

同じ GET 条件に合うレコードが0件のときは中断する。この例では「利用中」が0件=すでに廃止済みなので、二重の廃止申請を防ぐガードも兼ねている。/k/v1/records で、app に別アプリのIDを指定するだけ。共通のキー(この例では申請番号)で検索する。

var query = '申請番号 = "' + applicationNumber + '"';
var response = await kintone.api(kintone.api.url('/k/v1/records', true), 'GET', {
  app: XXX,       // 別アプリのアプリID
  query: query
});

並び順を指定しないとレコード番号の大きい順(=新しい順)で返るので、先頭の1件が最新になる。kintone.api() は操作している人の権限で動くため、別アプリの閲覧権限が必要。

3. 種別によって取り込む項目を切り替える

コピー元レコードの種別(この例では回線種別)を見て、その種別で使わないフィールドを空にする。先に全部入れてから不要なものを空にすると、条件分岐が少なく済む。

var dropdown1 = baseRecord['ドロップダウン_1'].value || '';   // 未選択でもエラーにしない

if (dropdown1.includes('フレッツ')) {
  record['A回線開通工事日'].value = null;      // フレッツでは使わない欄
} else {
  record['日時_10'].value = null;               // フレッツ以外では使わない欄
  record['日時_12'].value = null;
}

判定は includes() で「フレッツ」を含むかどうかだけ見ている。「フレッツ 光ネクスト」のように選択肢が細かく分かれていても、まとめて扱える。

JavaScript全体(アプリAの設定 →「JavaScript / CSSでカスタマイズ」でアップロードする。フォームには要素ID extraction01 のスペースを置く)

/*
 * 開設時情報の取り込みボタン(アプリA:回線工事申請アプリに設置)
 *
 * 廃止申請の編集画面にボタンを置き、押すと次の情報を画面に取り込む。
 *   - アプリB(案件申請アプリ)の工事日時
 *   - アプリA内の開設時レコードの回線情報
 *   - 担当者の名前・メール・電話
 * 取り込むのは画面だけで、保存はユーザーが行う。
 */
(function () {
  "use strict";

  // ------------------------------------------------------------
  // 1. 編集画面を開いたときに、ボタンを出すかどうかを判定する
  // ------------------------------------------------------------
  kintone.events.on('app.record.edit.show', function(event) {

    // ログイン中のユーザーのログイン名を取得
    var loginUser = kintone.getLoginUser();
    var loginUserName = loginUser.code;

    // ボタンを使える担当者(ログイン名)。ここにない人にはボタンを表示しない
    var allowedUsers = ['user_a', 'user_b'];

    // 申請種別(新規/廃止など)を取得
    var applicationType = event.record['申請種別1'].value;

    // 許可した担当者で、かつ「廃止」の申請のときだけボタンを表示する
    // (新規申請では開設時の情報が存在しないため)
    if (allowedUsers.includes(loginUserName) && applicationType === '廃止') {

      // ボタンを作る
      var myMenuButton = document.createElement('button');
      myMenuButton.id = 'myButtonId';
      myMenuButton.innerHTML = '開設時情報を抽出';

      // フォームに置いたスペース(要素ID:extraction01)にボタンを入れる
      var spaceElement = kintone.app.record.getSpaceElement('extraction01');
      if (spaceElement) {
        spaceElement.appendChild(myMenuButton);
      }

      // ----------------------------------------------------------
      // ボタンを押したときの処理
      // ----------------------------------------------------------
      myMenuButton.onclick = async function() {
        // 画面のレコード情報(ここに値を入れて最後に画面へ反映する)
        var record = event.record;

        // アプリBとつなぐキーになる申請番号
        var applicationNumber = record['申請番号'].value;

        // --------------------------------------------------------
        // 2. 確認ダイアログ。取り込み後に、日時の開始時刻を入力し直す必要があることも伝える
        // --------------------------------------------------------
        var userConfirmed = confirm('開設時の情報を抽出し取り込みします\n取り込まれたら\n・A回線フレッツ開通工事/撤去工事日時\n・B回線フレッツ開通工事/撤去工事日時\n・A回線開通工事/撤去工事日時\n・B回線開通工事/撤去工事日時\nに00:00以外の開始時間を入力してください');
        if (!userConfirmed) {
          return;   // キャンセルなら何もしない
        }

        // --------------------------------------------------------
        // 3. アプリB(案件申請アプリ)から工事日時を取得する
        //    申請番号が同じレコードを探す。読み取りのみで、アプリBには書き込まない
        // --------------------------------------------------------
        var query = '申請番号 = "' + applicationNumber + '"';
        var response = await kintone.api(kintone.api.url('/k/v1/records', true), 'GET', {
          app: XXX,       // アプリBのアプリID
          query: query    // 並び順の指定なし → レコード番号の大きい順(新しい順)で返る
        });

        if (response.records.length > 0) {
          // 複数ヒットした場合は先頭(最新)のレコードを使う
          var constructionDate = response.records[0]['工事日時'].value;

          // 回線種別がまだ分からないので、いったん全部の日時フィールドに入れる
          // (使わない欄は手順6で空にする)
          record['日時_9'].value = constructionDate;
          record['日時_11'].value = constructionDate;
          record['日時_10'].value = constructionDate;
          record['日時_12'].value = constructionDate;
          record['A回線開通工事日'].value = constructionDate;
          record['B回線開通工事日時'].value = constructionDate;

          // 画面に反映
          kintone.app.record.set(event);
        }

        // --------------------------------------------------------
        // 4. アプリA(このアプリ)から、同じビルの開設時レコードを取得する
        //    条件:ビルコードが同じ + ステータスが「利用中」
        //    並び順:申請日(日付_1)の新しい順
        // --------------------------------------------------------
        var buildingCode = record['ビルコード'].value;
        var query2 = 'ビルコード in ("' + buildingCode + '") and ラジオボタン_23 in ("利用中") order by 日付_1 desc';
        var records = await kintone.api(kintone.api.url('/k/v1/records', true), 'GET', {
          app: kintone.app.getId(),   // このアプリ自身のID(直書きしないのでアプリを複製しても動く)
          query: query2
        });

        // 「利用中」が0件 = すでに廃止済みの拠点。二重の廃止申請を防ぐため中断する
        // ※ 手順3の工事日時は画面に残る。足りない項目は手入力してから保存する
        if (records.records.length === 0) {
          alert('開設時のレコードのステータスが「廃止済」です\n重複拠点の廃止申請防止のため情報の取り込みはできません');
          return event;
        }

        // --------------------------------------------------------
        // 5. 開設時レコードから回線情報をコピーする
        // --------------------------------------------------------

        // コピーするフィールド(A回線・B回線それぞれの回線種別、対象コード、回線番号など)
        var fieldsToCopy = [
          'ドロップダウン_1',      // A回線の回線種別
          '文字列__1行__14',
          '文字列__1行__18',
          'A回線_対象コード',
          '文字列__1行__80',
          'A回線_回線番号',        // フレッツのみで使う項目。ダークなら元から空
          'ドロップダウン_2',      // B回線の回線種別
          '文字列__1行__7',
          '文字列__1行__34',
          'B回線_対象コード',
          '文字列__1行__82',
          'B回線_回線番号'         // フレッツのみで使う項目。ダークなら元から空
        ];

        // まず最新のレコードから全項目をコピー
        var baseRecord = records.records[0];
        fieldsToCopy.forEach(function(field) {
          record[field].value = baseRecord[field].value;
        });

        // 最新レコードで空だった項目は、2件目以降の古いレコードから順に補う
        // (増設や変更で申請が分かれ、最新レコードに一部の項目しかない場合への対応)
        for (var i = 1; i < records.records.length; i++) {
          var nextRecord = records.records[i];
          fieldsToCopy.forEach(function(field) {
            if (!record[field].value || record[field].value === '') {
              record[field].value = nextRecord[field].value;
            }
          });
        }

        // --------------------------------------------------------
        // 6. 回線種別に合わせて、使わない日時フィールドを空にする
        //    フレッツ回線と、ダークファイバーなどそれ以外の回線では使う日時欄が違う
        // --------------------------------------------------------

        // 回線種別を取得。未選択(null)でも .includes() でエラーにならないよう '' にする
        var dropdown1 = baseRecord['ドロップダウン_1'].value || '';   // A回線
        var dropdown2 = baseRecord['ドロップダウン_2'].value || '';   // B回線

        if (dropdown1.includes('フレッツ')) {
          // A回線がフレッツ → フレッツでは使わない「A回線開通工事日」を空にする
          record['A回線開通工事日'].value = null;
        } else {
          // A回線がフレッツ以外(ダークファイバーなど) → この回線では使わない日時欄を空にする
          record['日時_10'].value = null;
          record['日時_12'].value = null;
        }

        if (dropdown2.includes('フレッツ')) {
          // B回線がフレッツ → フレッツでは使わない「B回線開通工事日時」を空にする
          record['B回線開通工事日時'].value = null;
        }

        // 画面に反映
        kintone.app.record.set(event);

        // --------------------------------------------------------
        // 7. 「ユーザー選択」で選んだ担当者の連絡先を入れる
        //    未選択のときは何もしない(value[0] を直接読むとエラーになるため確認する)
        // --------------------------------------------------------
        var users = record['ユーザー選択'].value;
        var selectedUser = users.length > 0 ? users[0].code : '';

        if (selectedUser === 'user_a') {
          record['文字列__1行__9'].value = '担当者A';             // 名前
          record['文字列__1行__10'].value = 'mail_a@example.com'; // メール
          record['文字列__1行__11'].value = '000-0000-000A';      // 電話
        } else if (selectedUser === 'user_b') {
          record['文字列__1行__9'].value = '担当者B';
          record['文字列__1行__10'].value = 'mail_b@example.com';
          record['文字列__1行__11'].value = '000-0000-000B';
        }

        // 画面に反映(保存はユーザーが行う)
        kintone.app.record.set(event);
      };
    }
  });
})();

他のアプリで使うときは、次の部分を置き換える。処理の流れはそのまま使える。

役割この例別の業務の例(契約の解約申請)変更する場所
ボタンを出す条件申請種別が「廃止」申請種別が「解約」applicationType === '廃止'
ボタンを使える人担当者A・B契約管理の担当者allowedUsers
自アプリで探すキービルコード顧客コードquery2 の条件
対象にするステータス利用中契約中query2 の条件
他アプリとつなぐキー申請番号契約番号query の条件
他アプリから取る値工事日時契約開始日response.records[0][...]
コピーする項目回線種別、対象コードなどプラン、契約内容などfieldsToCopy
切り替えの判定回線種別に「フレッツ」を含むかプランに「法人」を含むかincludes('フレッツ') とその中のフィールド
ボタンの置き場所スペース extraction01同じgetSpaceElement()

フィールドコードは記事用に置き換えている。使うときは自分のアプリのフィールドコードに合わせる。

  • ボタンを押す人に、他アプリの閲覧権限が必要。ないと取得に失敗する
  • 自アプリで条件に合うレコードが0件のときは中断するが、その前に取得した他アプリの値は画面に残る。足りない項目は手入力してから保存する
  • event.record は編集画面を開いた時点の値。ボタンを押す前に手入力した内容は消える。入力後に押す運用なら kintone.app.record.get() で取り直す
  • エラー処理は入れていない。必要なら try / catch で囲む
  • GET /k/v1/records は指定がなければ最大100件まで。条件に合うレコードが100件を超える場合は別の方法が必要

本番に反映する前に、テスト用のアプリで動作を確認するのを推奨。

NOTICES

  • 記事内容は実装させたものがほとんどですが自己責任で参考にしてください。

TO HEADER