kintoneの案件数は3件なのに、担当者別グラフの件数を足すと4件になる。こんな場合は、集計の誤りと決めつける前に、1つのレコードが複数の分類に入る設計かを確認します。
この記事では、自社開発のダッシュボードプラグインの公開版1.1.4のコードを題材に、ユーザー選択フィールドを担当者別に分類する考え方を解説します。APIでレコードを取得する部分やグラフ描画は省き、取得済みの配列をどう数えるかに絞ります。
ユーザー選択のvalueは配列
ユーザー選択フィールドには、複数のユーザーを設定できます。値の形は公式のフィールド形式で確認できます。
const records = [
{ 担当: { type: 'USER_SELECT', value: [
{ code: 'a', name: '担当A' },
{ code: 'b', name: '担当B' }
] } },
{ 担当: { type: 'USER_SELECT', value: [
{ code: 'a', name: '担当A' }
] } },
{ 担当: { type: 'USER_SELECT', value: [] } }
];
この3レコードを「各担当者が関わる案件の件数」として数えると、担当Aが2件、担当Bが1件、未入力が1件です。分類ごとの合計は4件ですが、元レコードは3件です。
ここで4件を「ユニークな案件数」と呼ぶと混乱します。指標名を「担当者別の関与件数」とするなど、何を数えた値かを明示します。
Mapのキーは名前ではなくユーザーコードにする
名前だけをキーにすると、同名の別ユーザーを同じ分類へまとめてしまいます。ここではcodeをキー、nameを表示ラベルにします。
また、1つのレコード内で同じコードを複数回処理しないように、行ごとにSetを作ります。レコード全体で1つのSetを共有すると、担当Aが複数案件に関与していても1件にしか数えられなくなるため、作る場所が重要です。
教材コード:元の件数と分類の件数を分けて返す
// 教材用:取得済みのレコードを担当者別の関与件数に分類する。
function countByUser(records, fieldCode) {
const groups = new Map();
for (const record of records) {
const field = record[fieldCode];
if (!field || field.type !== 'USER_SELECT' || !Array.isArray(field.value)) {
throw new Error(`ユーザー選択を確認してください: ${fieldCode}`);
}
const users = field.value;
const keys = new Set(users.length ? users.map(user => user.code) : ['']);
for (const code of keys) {
if (typeof code !== 'string' || (users.length && !code)) {
throw new Error('ユーザーコードを取得できません');
}
const user = users.find(user => user.code === code);
if (!groups.has(code)) {
groups.set(code, {
code,
label: code ? user.name || code : '(未入力)',
count: 0
});
}
groups.get(code).count += 1;
}
}
const rows = [...groups.values()];
return {
recordCount: records.length,
assignmentCount: rows.reduce((sum, group) => sum + group.count, 0),
groups: rows
};
}
上の例のrecordsと合わせて実行すると、次の結果になります。
const result = countByUser(records, '担当');
console.log(result.recordCount); // 3
console.log(result.assignmentCount); // 4
console.log(result.groups);
// [
// { code: 'a', label: '担当A', count: 2 },
// { code: 'b', label: '担当B', count: 1 },
// { code: '', label: '(未入力)', count: 1 }
// ]
recordCountは入力配列の長さです。同じレコードを重複して取得していない前提で使います。このコードがレコードIDの重複を取り除くわけではありません。
assignmentCountは、未入力の分類も含む分類件数の合計です。案件の総数とは分けて扱います。未入力を集計対象から除くかどうかも、業務のルールとして決めてください。
未入力と未取得を同じにしない
value: []は、ユーザー選択フィールドを取得できているが、誰も選ばれていない状態として扱います。一方、フィールド自体が存在しない場合は、コード・取得対象・閲覧権限などの確認が必要です。
教材では、フィールドがない場合や型が違う場合は停止します。未取得を「未入力」に混ぜると、権限や取得条件の問題を、未割り当て案件として数えてしまうためです。コード未取得も同様に停止します。
売上を担当者別に合計するときは、配分のルールも必要
件数だけでなく、1件100,000円の案件を担当A・担当Bの両方にそのまま集計すれば、担当別合計を足すと200,000円になります。
これは担当別の関与金額を見たいのか、会社の売上総額を見たいのかで意味が変わります。会社全体の売上総額を出したい場合は、担当者別の棒の合計ではなく、対象レコードの金額を1回ずつ集計します。
担当者ごとの売上配分が必要なら、主担当を1人に決める、明細や別フィールドへ配分額を持たせるなど、先にデータ設計を決めます。担当者の数で自動的に均等配分される機能を、この教材や紹介するプラグインが持つという説明ではありません。
公開製品のコードと照合した範囲
公開配布中のZIPからjs/core.jsを取り出し、ローカルの対象ファイルと一致することを確認しました。製品のaggregate()も、ユーザー選択の各要素を分類へ入れ、同じレコード内の分類キーをSetで重複除外しています。
教材コードと公開製品コードに対し、元3件と分類4件の違い、同一レコード内の同じコード、同名・別コード、空配列、未取得など、計10項目をNode.jsで確認しました。入力が不正な場合の停止処理は教材で追加した範囲もあり、製品と教材のすべての振る舞いが同一という意味ではありません。
この検証には実機kintoneへの変更やグラフ画面の試験は含みません。また、取得範囲・期間・権限・取得上限が違えば、分類以前に対象レコードの件数が変わります。比較するときは、対象レコードをそろえてから分類の違いを見ます。
画面で確認したい場合
自社で開発・提供しているダッシュボードプラグインでは、指標カード・グラフ・表を一覧画面上部へ配置できます。設定・利用画面と対応範囲は以下で確認できます。
無料利用では、有償プランの案内バナーがPC・モバイルの一覧画面に30%の確率で表示されます。契約済みドメインには表示されません。利用前に、対応する項目・画面・取得上限を確認してください。