アンチパターンから学ぶ ワークフローの可視化

目次

はじめに

kintoneのカスタマイズでは、プロセス管理の情報をレコード画面に表示するケースがあります。
たとえば、ステータスの履歴の可視化、現在の作業者名の表示、プロセスフローの表示などが該当します。

こうしたカスタマイズには、一見動作しているように見えても、リスクを抱える実装パターンが存在します。

この記事では、ワークフロー情報の取得について、以下の2つを解説します。

  • アンチパターンとそのリスク
    DOM操作でkintoneのUI要素からステータスの履歴を取得するカスタマイズは、DOM構造への依存やUIへの副作用といったリスクがあります。
  • 推奨される実装方法とその利点
    プロセス管理のJavaScript APIを使用することで、DOM操作に頼らずステータスの履歴を正確に取得できます。

アンチパターンと推奨される実装方法を、サンプルコードを交えて比較しながら紹介します。
既存のカスタマイズのリスク確認や、新たに実装する際の参考にしてください。

また、補足として、REST APIの代わりに使用できるプロセス管理の便利なJavaScript APIも紹介します。

今回紹介するAPI

API 概要
kintone.app.record.getStatusHistory() ステータスの履歴を取得
いつ・誰が・どのステータスに変更したかを配列で返す
kintone.app.record.getAssignees() 現在の作業者を取得
プロセス管理で割り当てられた作業者の情報を返す
kintone.app.getStatus() プロセス管理の設定を取得
ステータス一覧やアクション定義をクライアント側で参照できる

ステータスの履歴を取得する

kintoneのカスタマイズで、ステータスがどのように変化してきたかを可視化することがあります。
たとえば、承認フローのタイムライン表示や、各ステータスにどれくらいの時間滞留したかを確認するケースです。

ここでは、次の図のようにレコード詳細画面でステータスの履歴をタイムライン形式で表示する実装を比較します。

サンプルコードで使用するスペース

要素ID フィールドタイプ
history_space スペース

アンチパターン:DOM操作によるステータスの履歴の取得

ステータスの変化を可視化する方法として、DOM操作でkintoneのUI要素から履歴情報を取得するパターンがあります。
「ステータスの履歴」の表示をプログラムから擬似的に操作し、表示されたテーブルの内容を読み取る実装です。

warning
注意

以下のコードはgetStatusHistory()を使った実装に置き換えることが推奨される書き方です。
新規のカスタマイズでは、後述するgetStatusHistory()を使用してください。

 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
// 非推奨: この書き方は getStatusHistory() を使った実装への置き換えを推奨します
(() => {
  'use strict';

  kintone.events.on('app.record.detail.show', async (event) => {
    const space = kintone.app.record.getSpaceElement('history_space');
    if (!space) {
      return event;
    }

    // 「ステータスの履歴」ボタンをDOM操作で取得し、クリックを擬似的に実行
    const historyButton = document.getElementsByClassName('gaia-app-statusbar-history');
    if (!historyButton.length) {
      return event;
    }
    historyButton[0].click();

    // ポップアップの出現を細かく監視し、表示された瞬間に非表示にする(表示時間を最小化)
    const popup = await new Promise((resolve) => {
      const timer = setInterval(() => {
        const el = document.querySelector('.gaia-app-statusbar-historypopup');
        if (el) {
          el.style.visibility = 'hidden';
          clearInterval(timer);
          resolve(el);
        }
      }, 10);
    });

    // 非表示のポップアップ内に描画されたテーブルからデータを取得
    const table = popup.querySelector('.listTable-gaia.gaia-app-status-table');
    if (!table) {
      return event;
    }

    const rows = table.querySelectorAll('tbody tr');
    space.style.whiteSpace = 'pre-line';
    rows.forEach((row, index) => {
      if (index > 0) {
        space.appendChild(document.createTextNode('\n'));
      }
      const cells = row.querySelectorAll('td');
      const date = cells[0]?.textContent?.trim() || '';
      const user = cells[1]?.textContent?.trim() || '';
      const status = cells[2]?.textContent?.trim() || '';
      const strong = document.createElement('strong');
      strong.textContent = `${user}: ${status}`;
      space.appendChild(document.createTextNode(`${date} `));
      space.appendChild(strong);
    });

    return event;
  });
})();

このアプローチには以下のリスクがあります。

リスク 説明
DOM構造への完全な依存 gaia-app-statusbar-historygaia-app-status-tableのようなクラス名はkintoneが実装上の都合でつけた名前であり、外部からの利用を想定していません。
これらのクラス名を頼りに複数のDOM要素を取得しているため、いずれかのクラス名がkintoneのアップデートで変更されるだけでカスタマイズ全体が動作しなくなります。
UIへの副作用 データ取得のためだけにステータス履歴のダイアログをJavaScriptで開閉しており、ユーザーが意図しないタイミングでUI操作が発生します。
画面のちらつきにつながるほか、kintoneのアップデートによってダイアログの挙動が変わった場合、非表示化処理が正しく機能しなくなるリスクもあります。
ユーザー情報が表示名に限られる テーブルのセルからテキストを取得しているため、ユーザーの表示名しか得られません。
ログイン名(code)が取得できないため、たとえばその後にAPIでユーザーアイコンを取得するような処理につなげることができません。
モバイル対応の困難性 モバイル版ではPC版とDOM構造が異なるため、同じクラス名やテーブル構造に依存したコードが動作しません。
モバイル対応には別途DOM解析が必要になり、保守コストが倍増します。

推奨される実装方法:getStatusHistory()によるステータスの履歴の取得

kintone.app.record.getStatusHistory()を使用することで、ステータスの履歴を正確に取得できます。
いつ・誰が・どのステータスに変更したかが配列で返されるため、タイムライン表示や滞留時間の確認を実装できます。

 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
(() => {
  'use strict';

  kintone.events.on('app.record.detail.show', async (event) => {
    const space = kintone.app.record.getSpaceElement('history_space');
    if (!space) {
      return event;
    }

    const statusHistory = await kintone.app.record.getStatusHistory();
    space.style.whiteSpace = 'pre-line';
    statusHistory.forEach((entry, index) => {
      if (index > 0) {
        space.appendChild(document.createTextNode('\n'));
      }
      const date = new Date(entry.changedAt).toLocaleString('ja-JP');
      space.appendChild(document.createTextNode(`${date} `));
      const strong = document.createElement('strong');
      strong.textContent = `${entry.assignees.map((a) => a.name).join('、')}: ${entry.status}`;
      space.appendChild(strong);
    });

    return event;
  });
})();

DOM操作によるステータスの履歴の取得と比較して、以下の点が改善されます。

改善点 説明
DOM構造に依存しない 専用のAPIがステータスの履歴情報を返すため、kintoneのクラス名やテーブル構造に依存しません。
kintoneのアップデートに影響を受けない保守性の高いカスタマイズを実現できます。
UIへの副作用がない ダイアログを開閉することなくクライアント側で直接データを取得します。
画面のちらつきや本来不要なUI操作が発生しません。
構造化されたユーザー情報を取得 変更日時、変更したユーザー、変更先のステータスが構造化されたデータで返されます。
ユーザーは表示名だけでなくログイン名(code)も含むため、APIでのユーザーアイコンの取得など後続の処理につなげられます。
モバイル版にも対応 kintone.mobile.app.record.getStatusHistory()で、PC版と同じ方法でステータスの履歴を取得できます。
DOM構造の違いを意識する必要がありません。
豊富な情報量 ステータスの変更に加えて、実行されたアクション名や変更時点の作業者情報も取得できます。
ワークフローの全体像を正確に把握できます。

getStatusHistory()のAPIドキュメントは、以下を参照してください。

kintone.app.record.getStatusHistory()

補足:REST APIの代わりに使えるプロセス管理のJavaScript API

プロセス管理には、getStatusHistory()以外にも便利なJavaScript APIがあります。
いずれもREST APIリクエストを発生させずに情報を取得できます。

ここでは、getAssignees()getStatus()の2つを紹介します。

これらのAPIで取得できる情報は、REST APIでも取得できます。
大量のユーザーがアプリに同時アクセスする環境では、その回数分のREST APIリクエストが発生するため、場合によっては1日の呼び出し上限に達するリスクがあります。

数十人規模の環境では影響は限定的ですが、数百人・数千人が利用するアプリでは注意してください。
getAssignees()getStatus()のJavaScript APIを使うことで、REST APIリクエストを発生させずに同じ情報を取得できます。
大規模環境での利用や将来的な拡張を見据える場合は、こちらのAPIの利用もご検討ください。

getAssignees():現在の作業者を取得する

kintone.app.record.getAssignees()を使用すると、プロセス管理で割り当てられた現在の作業者を取得できます。

/v1/records.jsonのREST APIでレコードを再取得して作業者フィールドを参照する代わりに、このAPIを使えばREST APIリクエストなしで直接取得できます。

以下は、レコード詳細画面でスペースフィールドに現在の作業者名を太字で表示するサンプルです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
(() => {
  'use strict';

  kintone.events.on('app.record.detail.show', async (event) => {
    const space = kintone.app.record.getSpaceElement('assignee_space');
    if (!space) {
      return event;
    }

    const assignees = await kintone.app.record.getAssignees();
    const names = assignees.map((a) => a.assignee.name).join('、');
    space.textContent = '現在の作業者: ';
    const strong = document.createElement('strong');
    strong.textContent = names;
    space.appendChild(strong);

    return event;
  });
})();

APIドキュメントは、以下を参照してください。

kintone.app.record.getAssignees()

getStatus():プロセス管理の設定を取得する

kintone.app.getStatus()を使用すると、プロセス管理のステータス一覧やアクション定義を取得できます。

/v1/app/status.jsonのREST APIでプロセス管理の設定を呼び出す代わりに、このAPIを使えばREST APIリクエストなしで取得できます。

以下は、レコード詳細画面でスペースフィールドにプロセス管理のステータスをフロー形式で表示し、現在のステータスを太字で強調するサンプルです。

 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
(() => {
  'use strict';

  kintone.events.on('app.record.detail.show', async (event) => {
    const space = kintone.app.record.getSpaceElement('flow_space');
    if (!space) {
      return event;
    }

    const statusSettings = await kintone.app.getStatus();
    const statuses = Object.values(statusSettings.states)
      .sort((a, b) => Number(a.index) - Number(b.index))
      .map((s) => s.name);
    const currentStatus = event.record['ステータス'].value;
    statuses.forEach((name, index) => {
      if (index > 0) {
        space.appendChild(document.createTextNode(' → '));
      }
      if (name === currentStatus) {
        const strong = document.createElement('strong');
        strong.textContent = `【${name}】`;
        space.appendChild(strong);
      } else {
        space.appendChild(document.createTextNode(name));
      }
    });

    return event;
  });
})();

APIドキュメントは、以下を参照してください。

kintone.app.getStatus()

実装時に押さえておきたいこと

各APIの非同期処理の扱い、利用画面、モバイル対応について案内します。
事前に把握しておくことで、実装後のトラブルを未然に防げます。

非同期処理の活用

今回紹介したAPIはすべて非同期なAPIです。
async/awaitを使用して順序どおりに処理してください。
詳しくは次のページを参照してください。

利用できる画面について

プロセス管理のAPIは、利用できる画面がAPIごとに異なります。
各APIのドキュメントで対応するイベントを確認してから実装してください。

モバイル版での利用について

この記事で紹介したAPIは、モバイル版でも利用できます。
モバイル版ではkintone.mobile名前空間のAPIを使用してください。

PC版 モバイル版
kintone.app.record.getStatusHistory() kintone.mobile.app.record.getStatusHistory()
kintone.app.record.getAssignees() kintone.mobile.app.record.getAssignees()
kintone.app.getStatus() kintone.mobile.app.getStatus()

まとめ

  • getStatusHistory()で、DOM操作に頼らずステータスの履歴を正確に取得できます。
  • getAssignees()で、REST APIリクエストなしに現在の作業者を取得できます。
  • getStatus()で、REST APIリクエストなしにプロセス管理の設定を参照できます。

プロセス管理のJavaScript APIを使用することで、ワークフローの可視化を安定して実装できます。
ワークフローの可視化では積極的にこれらのAPIを活用してください。

information

このTipsは、2026年5月版kintoneで動作を確認しています。