関連レコードのデータをCSV出力する方法

著者名:楊 思夢(サイボウズ株式会社)

目次

はじめに

こんにちは。サイボウズの楊です。
今回は関連レコードもCSV出力したい!というニーズにお応えしたいと思います。

kintoneアプリでは、他アプリのレコードを参照できる関連レコード一覧機能があります。
ひとつのフィールドをキーとし、関連するレコードを現在のレコード詳細画面に表示できます。
しかし、この場合関連レコードのデータは表示させているだけで、閲覧しているレコードにデータとして所持している訳ではありません。

またkintoneにはデータをCSV形式でダウンロードする機能があります。
レコードをCSVファイルで書き出す場合、関連レコードはそのデータを保持していないため、通常は関連レコードをCSV出力できません。

今回は、関連レコードを含めたCSVを出力するTipsを紹介できればと思います。

デモ環境

デモ環境で実際に動作を確認できます。

ログイン情報は cybozu developer networkデモ環境で確認してください。

完成イメージ

完成したアプリには、以下のようにCSVファイル出力ボタンが表示されます。

ダウンロードしたCSVファイルのイメージは以下のとおりです。

アプリの準備

今回使用するのはアプリストアにある「案件管理」と「顧客リスト」アプリを応用したものです。
のちほど「顧客リスト」アプリの関連レコードのフィールド設定で「案件管理」アプリが必要となりますので、先に「案件管理」のアプリの追加をしてください。

案件管理アプリ

フィールドコードが異なると、正常に動作しないため注意して変更しましょう。
案件管理アプリのフィールドの設定は以下のとおりです。
なお、記載していないフィールドはデフォルトのままです。

フィールドタイプ フィールド名 フィールドコード
文字列(1行) 会社名 company
ドロップダウン 製品名 product
計算 小計 subtotal

顧客リストアプリ

顧客リストアプリに「関連レコード」フィールドを追加します。

関連レコード一覧の設定の詳細は以下のとおりです。

フィールドコードが異なると、正常に動作しないため注意して変更しましょう。
顧客リストアプリのフィールドの設定は以下のとおりです。
なお、記載していないフィールドはデフォルトのままです。

フィールドタイプ フィールド名 フィールドコード
文字列(1行) 会社名 company
文字列(1行) 部署名 division
文字列(1行) 担当者名 assignee
文字列(1行) TEL TEL
文字列(1行) メールアドレス Mail
関連レコード一覧 関連レコード一覧 reference

サンプルコード

サンプルコードは以下のとおりです。
JSファイルはURL指定もしくはローカルから「顧客リスト」アプリに適用してください。

  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
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
 * related records sample program
 * Copyright (c) 2014 Cybozu
 *
 * Licensed under the MIT License
*/

(() => {
  'use strict';

  kintone.events.on('app.record.index.show', (event) => {
    const records = event.records;
    const appId = kintone.app.getRelatedRecordsTargetAppId('reference');

    // ボタンの有無をチェック
    if (document.getElementById('btn-export-csv')) {
      return;
    }

    const spaceEl = kintone.app.getHeaderMenuSpaceElement();
    const CSVButtonEl = document.createElement('button');
    CSVButtonEl.textContent = 'CSVファイル出力';
    CSVButtonEl.id = 'btn-export-csv';
    spaceEl.appendChild(CSVButtonEl);

    CSVButtonEl.addEventListener('click', () => {
      getMakeCsv(appId, records).then((resp) => {
        downloadFile(resp);
      });
    });
  });

  // csvデータの作成
  const getMakeCsv = (appId, customerRecords) => {
    // 同じ会社名の関連レコードを取得
    const fetchRelatedRecords = (opt_index, opt_data) => {
      const index = opt_index || 0;
      const data = opt_data || [];
      const customerRecord = customerRecords[index];

      const company = customerRecord.company.value;
      const division = customerRecord.division.value;
      const assignee = customerRecord.assignee.value;
      const tel = customerRecord.TEL.value;
      const mail = customerRecord.Mail.value;

      const query = `company = "${company}"`; // 会社名をキーに該当関連レコードを取得

      const params = {
        app: appId,
        query: query
      };
      return kintone.api(kintone.api.url('/k/v1/records', true), 'GET', params)
        .then((resp) => {
          const row = [company, division, assignee, tel, mail];
          const relatedRecords = resp.records;
          if (relatedRecords.length > 0) {
            relatedRecords.forEach((relatedRecord) => {
              row.push(relatedRecord.product.value);
              row.push(relatedRecord.subtotal.value);
            });
          }
          data.push(row.join(','));
          if (customerRecords.length > index + 1) {
            return fetchRelatedRecords(index + 1, data);
          }
          return data;
        });
    };
    const header = ['会社名', '部署名', '担当者名', 'TEL', 'メールアドレス', '製品名', '小計'].join(',');
    let csvData = [header];

    return new kintone.Promise((resolve, reject) => {
      fetchRelatedRecords().then((resp) => {
        csvData = csvData.concat(resp);
        resolve(csvData);
      });
    });
  };

  // ダウンロード関数
  const downloadFile = (data) => {
    const csv = data.join('\r\n');
    // ファイル名
    const filename = `client_case${getTimeStamp()}.csv`;

    // Blob準備
    const bom = new Uint8Array([0xef, 0xbb, 0xbf]);
    const blob = new Blob([bom, csv], {type: 'text/csv'});

    if (window.navigator.msSaveBlob) {
      window.navigator.msSaveBlob(blob, filename);
    } else {
      const e = new MouseEvent('click', {view: window, bubbles: true, cancelable: true});
      const url = window.URL || window.webkitURL;
      const blobUrl = url.createObjectURL(blob);
      const a = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
      a.href = blobUrl;
      a.download = filename;
      a.dispatchEvent(e);
    }
  };

  // ファイル名に付与する日付の取得
  const getTimeStamp = () => {
    const d = new Date();
    const YYYY = d.getFullYear();
    let MM = d.getMonth() + 1;
    let DD = d.getDate();
    let hh = d.getHours();
    let mm = d.getMinutes();
    if (MM < 10) {
      MM = `0${MM}`;
    }
    if (DD < 10) {
      DD = `0${DD}`;
    }
    if (hh < 10) {
      hh = `0${hh}`;
    } else if (mm < 10) {
      mm = `0${mm}`;
    }
    return `${YYYY}${MM}${DD}${hh}${mm}`;
  };
})();

コードの説明

今回はレコードの詳細情報を取得し、CSVファイルを手動で作成して、そのCSVファイルの中にデータを格納しています。
それでは簡単に説明します。

レコード情報と要素の取得

kintone.app.getRelatedRecordsTargetAppId('フィールドコード')で関連レコードのアプリIDを取得します。
関連レコード一覧の参照先のアプリIDを取得する

13
const appId = kintone.app.getRelatedRecordsTargetAppId('reference');

そして、kintone.app.getHeaderMenuSpaceElement()でボタン作成のためにレコード一覧のメニューの右側の要素を取得します。
レコード一覧のメニューの右側の要素を取得する

20
const spaceEl = kintone.app.getHeaderMenuSpaceElement();

customerRecordsは、顧客管理アプリを一覧表示したときのevent.recordsです。
indexでひとつずつレコードを取り出し、各変数の中にフィールドの値を格納します。

39
const customerRecord = customerRecords[index];

queryでは、検索条件でキーとなる会社名を変数に格納します。
複数のレコードを取得する

47
const query = `company = "${company}"`; // 会社名をキーに該当関連レコードを取得

ボタンの生成と処理

document.getElementById('btn-export-csv')をif文の条件とすることで、ボタンがない場合のみボタン要素を生成し、増殖バグを防ぎます。
増殖バグを防ぐ処理については、 レコード一覧画面にボタンを配置してみようを参照してください。

15
16
// ボタンの有無をチェック
if (document.getElementById('btn-export-csv')) {

以下でボタンを押した際に処理を行います。

26
CSVButtonEl.addEventListener('click', () => {

CSVファイルの作成

CSVファイルの各項目名(ヘッダー)は手動で作成する必要があります。
CSVはカンマ区切りなので、join(',')で配列の要素を結合した文字列を生成します。

70
const header = ['会社名', '部署名', '担当者名', 'TEL', 'メールアドレス', '製品名', '小計'].join(',');

関連レコードはkintone.apiで取得します。
kintone.apiは非同期実行なので、顧客ごとの関連レコードを取得するためfetchRelatedRecordsを再帰的に呼び出します。
取得した関連レコードの内容をCSVの各行に追加します。

35
36
// 同じ会社名の関連レコードを取得
const fetchRelatedRecords = (opt_index, opt_data) => {

現在の日付をファイル名に

Dateオブジェクトで現在の日時を取得します。

106
const d = new Date();

getMonth()は前の月を取得するため、注意して+1します。

108
let MM = d.getMonth() + 1;

月や日付の桁数が一桁場合、前に0を足して二桁にします。

112
113
114
115
116
117
118
119
120
121
122
if (MM < 10) {
  MM = `0${MM}`;
}
if (DD < 10) {
  DD = `0${DD}`;
}
if (hh < 10) {
  hh = `0${hh}`;
} else if (mm < 10) {
  mm = `0${mm}`;
}

最後にYYYY + MM + DD + hh + mmの形式で返します。

123
return `${YYYY}${MM}${DD}${hh}${mm}`;

データのダウンロードについては 集計したデータをCSVでダウンロードするにはを参照してください。

最後に

いかがでしたでしょうか。
これで関連レコードもCSV出力できるようになりました。
また上記のテクニックを活用することで関連レコードに限らず、自由に他アプリからのデータをCSV出力できます。
ぜひお試しください。

information

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