kintoneの標準機能だけでエクセル帳票の出力をやってみた

ここでいう標準機能とはスタンダードプランのカスタマイズのみを使うということです。

# 目的
kintoneのレコードとして記録されたデータを帳票としてエクセルで出力したかったので、作ってみました。
帳票のエクセルは多くの人が自作できるので、そのエクセル自体をフォーマットとしてkintoneに保存しといたらどうかなと思ったのがきっかけです。

# 方法
アプリを２つ作ります。
1. エクセル帳票出力：エクセルに書き込みたいデータを持っているアプリ
1. エクセル帳票フォーマット：エクセルのフォーマットを保存しているアプリ

1から2のフォーマットを呼び出して、データを書き込み、ローカルに保存するという流れです。

# 条件
ブラウザはChromeです。
kintone UI ComponentとExceljsを使用しました。

![スクリーンショット 2022-12-22 1.29.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3002870/ef4c3a10-0352-c974-c54a-9737265517d7.png)


# エクセル帳票出力
こんなのです。
kintone UI Componentでボタンをつけました。
このボタンを押すと上記の流れが起こるという仕組みです。
kintone UI Componentについては

https://developer.cybozu.io/hc/ja/articles/900005544623-kintone-UI-Component-v1

![スクリーンショット 2022-12-22 0.49.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3002870/396783ff-eaa1-81ed-8b0f-0c4a77c07cb7.png)

# エクセル帳票フォーマット
こんなのです。
![スクリーンショット 2022-12-22 0.54.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3002870/3df7a6b7-fa76-e33b-728d-05c6cb7c4594.png)

seikyu_syo.xlsxは、一応フォーマットなので「御請求書」とだけ書いておきました。
そのエクセルファイルを添付ファイルでkintoneに保存したという訳です。
![スクリーンショット 2022-12-22 0.58.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3002870/71b2938c-e4aa-3dbe-6249-50f703d0edba.png)

# エクセル帳票出力のカスタマイズ

ボタンをおすと
getData
dlFile
writeXlsx
saveLocal
という４つの関数が順番に動くというものです。

~~~javascript:index.js
kintone.events.on("app.record.detail.show", function (event) {
  
  // ボタンの作成と配置
  const hmsp = kintone.app.record.getHeaderMenuSpaceElement();
  const btn = new Kuc.Button({
    text: "帳票出力",
    type: "submit",
    className: "options-class",
    id: "options-id",
    visible: true,
    disabled: false,
  });
  hmsp?.appendChild(btn);

  // ボタンを押したとき
  btn.addEventListener("click", async function () {
    new kintone.Promise(function (resolve) {
      // GETリクエストパラメータの設定
      const body = {
        app: 119, // 帳票フォーマットアプリid（それぞれ違うので変えてください）
        query: 'レコード番号 = "1"', // フォーマットを保存しているレコード番号でデータをGET
      };
      resolve(body);
    })
      .then(getData) // 帳票フォーマットアプリからレコード番号１をget
      .then(dlFile) // getしたデータにあるfileKeyをつかって、ファイルダウンロードAPIを実行してレスポンスをもらう
      .then(writeXlsx.bind(null, event)) // excelに書き込む。レコードのデータeventを第１引数として渡す
      .then(saveLocal); // レスポンスをローカルにセーブする（自分のPCにダウンロードされる）
  });
});
~~~

~~~javascript:index.js
function getData(requestParam) {
  return new kintone.Promise(function (resolve, reject) {
    kintone.api(
      kintone.api.url("/k/v1/records", true),
      "GET",
      requestParam,
      function (resp) {
        resolve(resp); // getしたデータを次のdlFile関数に渡します
      },
      function (err) {
        console.log(err);
      }
    );
  });
}

// getData関数からデータをrespで受け取ります。
function dlFile(resp) {
  return new kintone.Promise(function (resolve, reject) {
    // fileKeyを取り出します
    const filekey = resp.records[0].添付ファイル.value[0].fileKey;
    // fileKeyをurlに設定します。
    const url = kintone.api.urlForGet("/k/v1/file", { fileKey: filekey }, true);
    // ファイルダウンロードAPI を実行します。
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
    xhr.responseType = "blob";

    xhr.onload = function () {
      if (xhr.status === 200) {
        // エクセルのフォーマットがレスポンスとして返却されます。
        console.log(xhr.response);
        resolve(xhr.response); // エクセルのフォーマットを次のwriteXlsx関数にわたす。
      }
    };
    xhr.send();
  });
}

// レコードのデータをeventで、エクセルのフォーマットをrespで受け取る
function writeXlsx(event, resp) {
  return new kintone.Promise(async function (resolve, reject) {
    const record = event.record;
    // Workbookの作成
    const workbook = new ExcelJS.Workbook();
    await workbook.xlsx.load(resp);
    // 最初のシートを取り出す
    const worksheet = workbook.worksheets[0];

    // セルへ記入
    worksheet.getCell("A2").value = record.名前.value;
    worksheet.getCell("B2").value = record.金額.value;

    // UInt8Arrayを生成

    const uint8Array = await workbook.xlsx.writeBuffer();
    resolve(uint8Array); // 次のsaveLocal関数に渡す
  });
}


function saveLocal(uint8Array) {
  return new kintone.Promise(function (resolve, reject) {
    // Blobオブジェクトにファイルを格納
    const blob = new Blob([uint8Array], {
      type: "application/octet-binary",
    });
    const url = window.URL || window.webkitURL;

    // BlobURLの取得
    const blobUrl = url.createObjectURL(blob);

    // リンクを作成し、そこにBlobオブジェクトを設定する
    const alink = document.createElement("a");
    alink.textContent = "ダウンロード";
    alink.download = "seikyuusyo.xlsx";
    alink.href = blobUrl;
    alink.target = "_blank";

    // マウスイベントを設定
    const e = new MouseEvent("click", {
      view: window,
      bubbles: true,
      cancelable: true,
    });

    // aタグのクリックイベントをディスパッチする
    alink.dispatchEvent(e);
  });
}
~~~

# 結果

A2とB2セルに名前と金額フィールドに書かれていたデータが記入されたエクセルファイルがダウンロードフォルダに入りました。
![スクリーンショット 2022-12-22 1.25.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3002870/771cdb87-cccb-7bfe-7095-a6d440852059.png)

# 最後に
kintoneもjavascriptも初心者ですが
kintoneが好きなので、何かお役に立つことがあればと思って書きました。

少人数でしか使わないので成り立つようなやり方をしているかもしれません。
ご指摘頂けたら有り難いです。