kintoneでレコード全件について関連レコードの項目を集計して更新する機能を泥臭くカスタマイズする【その1】

前回、「詳細画面を開いたら関連レコードの項目を集計して保存する」という記事を書きました。

https://qiita.com/juri_don/private/0f796fe0ba61fe6ae0eb

今回はコレを泥臭く魔改造？して、一覧でボタンをクリックしたら全レコードについて関連レコードの項目を集計して更新するようなカスタマイズを目指そうと思います。

今回は泥臭いのがポイントです。
ライブコーディングみのある泥臭いコーディング・・・。あとできれいなコードにします👀

※書いていたら想定通り長くなったので・・・次回、次次回と続くと思います👀
※今回の目標は一覧画面から1件集計＆更新するということ。です。

**見出しの記号の説明**
🙄：考えるところ
🛠️：手を動かすところ


# 🛠️アプリの準備

というわけで、まずは準備としてコチラのアプリを完成させてください👀

[kintoneで詳細画面を開いたら関連レコードの項目を集計して保存する](https://qiita.com/juri_don/private/0f796fe0ba61fe6ae0eb)

3つのアプリが追加されていると思いますが、改造するのは「顧客管理（営業支援パック）」アプリです。

一覧に「Aプラン小計」フィールドを追加しておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/617fa6c2-7089-92fd-0e6e-89e91622877a.png)


# 🙄改造の仕様や方針

では、どんなふうに改造するか考えます。

##🙄やりたいこと
**一覧の「ボタン」をクリックしたら、Aプラン小計を全レコードについて計算して、全レコード更新する**

##🙄やりたいことの実現のために必要な機能

* 一覧にボタンを設置する
* ボタンをクリックしたらレコード全件集計して全件更新する

とりあえず、このくらい考えた時点でできるところから作っていきましょう。

# 🛠️一覧にボタンを設置する

## 🛠️一覧なので、一覧表示後イベントを書く

とりあえず一覧表示イベントのおまじないを書きます。おまじないではないけど。
一覧表示されたらこれが実行されます。

```js
// 一覧表示
kintone.events.on(["app.record.index.show"], (event) => {
  // ここに一覧表示後の処理を書く

});

```

## 🛠️一覧にボタンを設置する
**kintone UI Component** を使ってボタンを設置します。
アプリの設定の、「JavaScript / CSSでカスタマイズ」に下記を追加しておきましょう。
`https://unpkg.com/kintone-ui-component/umd/kuc.min.js`

私は自分で過去に書いたコードのざっくりサンプルを沢山持っておりまして、
その中から **kintone UI Component を使ってボタン設置** するコードを使います。

```js
// ボタンを設置するコード
const sp = kintone.app.record.getHeaderMenuSpaceElement();
const record = event.record;
const btnA = new Kuc.Button({
  text: "ボタン",
  type: "submit",
});
sp?.appendChild(btnA);
btnA.addEventListener("click", async () => {});
```

ただコピーしてきただけなのでちょいちょい気になるところがあります。
`const sp = kintone.app.record.getHeaderMenuSpaceElement();`は一覧用ではないので書き換えます。
↓コチラを参考に一覧のメニュー下の情報を取るように書き換えます。
https://developer.cybozu.io/hc/ja/articles/201942004#step5

また、btnAのAとかもきになるし、recordもとりあえず今はいらないので消します。
というわけで、一覧にボタンを設置するコードはこんな感じになります。

```js
// ここに一覧表示後の処理を書く
const sp = kintone.app.getHeaderMenuSpaceElement();
const btn = new Kuc.Button({
  text: "ボタン",
  type: "submit",
});
sp?.appendChild(btn);

// ボタンクリックした時
btn.addEventListener("click", async () => {
  // ボタンクリックした時のコード
});
```

## 🛠️ボタン増殖対策をする

ここで、忘れてはいけないのが、「ボタン増殖対策」
ページめくりしたり、一覧の並び替えをするとボタンが増えていくのです・・・。
※ボタンじゃなくても、テキストボックスとかでも増殖しちゃうよ！

↓こちらにボタン増殖対策について書いてあるので読んでみましょう！
[第2回 レコード一覧画面にボタンを置いてみよう！](https://developer.cybozu.io/hc/ja/articles/201767270)

既にボタンがあったらボタンを再び設置しないようにするために、ボタンに`idプロパティ`をもたせて、
指定のidの要素があれば画面を更新しないようにします。

先程のリンク先からこの部分のコードをコピペしてきます。

```js
if (document.getElementById('my_index_button') !== null) {
  return;
}
```



idは絵文字にしてみました。
これでボタンは増殖しなくなりました。

```js
// ここに一覧表示後の処理を書く
if (document.getElementById("👀") !== null) {
  return;
}
const sp = kintone.app.getHeaderMenuSpaceElement();
const btn = new Kuc.Button({
  text: "ボタン",
  type: "submit",
  id: "👀",
});
sp?.appendChild(btn);
// ボタンクリックした時
btn.addEventListener("click", async () => {
  // ボタンクリックした時のコード
});
```

**ここまで書いたら、JavaScriptをupして動かしてみてね！**


# 🙄ボタンをクリックしたらレコード全件集計して全件更新する？

次はボタンをクリックしたときの動きを作るぞー！！！
というところなのですが、ちょっと待ってください。

**レコード全件集計して全件更新する**

って、コードをどんなふうに組み立てたらできるのか、どこから手を付けたらいいのか分からないですよね💦💦
というわけで下記のように、少しずつステップを踏みながら作っていこうと思います。

1. まずはどれか特定の1件だけ集計＆更新できるようにしてみる（今回）
1. 特定の2～3件集計＆更新してみる（次回）
1. 全件（100件以内）集計＆更新してみる（最終回）
1. 100件を超えた場合も使えるように改造してみる（がんばって！）

# 🛠️特定の1件だけ集計＆更新できるようにしてみる

ではまず、特定のレコード1件について考えてみます。
[前回-kintoneで詳細画面を開いたら関連レコードの項目を集計して保存する](https://qiita.com/juri_don/private/0f796fe0ba61fe6ae0eb)では、
「関連レコードの項目を集計した結果」を「Aプラン小計フィールド」に入れて更新するのでした。

と、一言で言っても下記3ステップ必要です。

* 関連レコードの項目を絞り込むパラメータを作る
* GETで関連レコードのレコードを取得する
* Aプラン小計に保存する集計値を計算する
* PUTでAプラン小計フィールドを更新する

というわけで、一つずつやっつけていきましょう。

## 🛠️関連レコードの項目を絞り込むパラメータを作る

関連レコード集計用リクエストパラメータ部分は↓こんなコードでしたが、コレを一覧画面でも使えるように改造します。

```js
const clientRecordId = event.recordId;
// 関連レコード集計用リクエストパラメータ準備
const relatedAppId = kintone.app.getRelatedRecordsTargetAppId("案件一覧");
const query =
  '顧客管理レコード番号_関連レコード紐付け用="' +
  clientRecordId +
  '" and 提案プラン in ("Aプラン")';
const outputFields = ["合計費用"];
const appUrl = kintone.api.url("/k/v1/records");
const params = {
  app: relatedAppId,
  query: query,
  fields: outputFields,
};
```


一覧画面なので `event.recordId` は使えません。
とりあえずはレコードIDベタ打ちします。レコード番号**19**のレコードを指定しましょう。
`kintone.app.getRelatedRecordsTargetAppId("案件一覧")`は使用できるのでそのまま。
また、kintone REST API Client を使うので、appUrlは不要になります。

というわけでリクエストパラメーターはこんな感じかな

```js
// 関連レコード集計用リクエストパラメータ準備
const clientRecordId = 19;
const relatedAppId = kintone.app.getRelatedRecordsTargetAppId("案件一覧");
const query =
  '顧客管理レコード番号_関連レコード紐付け用="' +
  clientRecordId +
  '" and 提案プラン in ("Aプラン")';
const outputFields = ["合計費用"];
const params = {
  app: relatedAppId,
  query: query,
  fields: outputFields,
};
```

ボタンをクリックしたときにはまず集計してほしいので、
こんな感じに書いて、`console.log(params);`を忍ばせてupして
一度アプリ上でボタンクリックしてみましょう。
コンソールにとりあえずリクエストパラメータらしきものが表示されたら良しとしましょう。

```js
// ボタンクリックした時
btn.addEventListener("click", async () => {
  // ボタンクリックした時のコード
  // 関連レコード集計用リクエストパラメータ準備
  const clientRecordId = 19;
  const relatedAppId = kintone.app.getRelatedRecordsTargetAppId("案件一覧");
  const query =
    '顧客管理レコード番号_関連レコード紐付け用="' +
    clientRecordId +
    '" and 提案プラン in ("Aプラン")';
  const outputFields = ["合計費用"];
  const params = {
    app: relatedAppId,
    query: query,
    fields: outputFields,
  };
  console.log(params);
});
```

うーん、多分、ヨシ！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/cf33182d-7640-1313-02d3-71f5e8023415.png)


## 🛠️GET（getRecords）で関連レコードのレコードを取得する

次は作成したパラメータで集計結果を取得するコードを書きます。

**kintone REST API Client** を使いたいので、
アプリの設定の、「JavaScript / CSSでカスタマイズ」に下記を追加しておきましょう。
`https://unpkg.com/@kintone/rest-api-client@latest/umd/KintoneRestAPIClient.min.js`

公式ドキュメントによると↓こんな感じ
https://github.com/kintone/js-sdk/blob/master/packages/rest-api-client/docs/record.md#getRecords

ですが、
[私が以前書いた記事](https://qiita.com/juri_don/items/d669762ed8cb281be7a5#getrecords%E8%A4%87%E6%95%B0%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E5%8F%96%E5%BE%97)のコードを流用します。

https://qiita.com/juri_don/items/d669762ed8cb281be7a5

↓この部分使います。

```js
  const client = new KintoneRestAPIClient();
  // resに複数レコード分取得
  const res = await client.record.getRecords({
    app: appId, 
    query: `$id in(${text_m.value}) order by $id asc`,
  });
```

await がついている部分は非同期の処理を待ってくれます。
`btn.addEventListener("click", async () => {` の async と対応しています。
詳しくはこのあたりを見てもらえるといいかも？↓

https://qiita.com/juri_don/items/bcb89d161cca80e98370


さて続きですが、先程の流用したい部分のリクエストパラメータ部分を、
そのまま`params`で置き換えると、↓こんな感じになります。

```js
  const client = new KintoneRestAPIClient();
  // resに複数レコード分取得
  const res = await client.record.getRecords(parmas);
```

## 🛠️Aプラン小計に保存する集計値を計算する

前回の記事で、合計金額はこのように求めていました。
`amount`に集計値が格納されます。
これを今回用に編集して流用します。

```js
// 合計金額集計
const amount = resp.records.reduce(
  (preV, p) => preV + parseFloat(p.合計費用.value),
  0
);
```

↓変更後（respをresに変えただけ）

```js
// 合計金額集計
const amount = res.records.reduce(
  (preV, p) => preV + parseFloat(p.合計費用.value),
  0
);
```

これをボタンクリックのコードの最後に入れたらこんな感じ。
集計値の確認もしたいので、`console.log(amount);` を忍ばせておきましょう。

```js
// ボタンクリックした時
btn.addEventListener("click", async () => {
  // ボタンクリックした時のコード
  // 関連レコード集計用リクエストパラメータ準備
  const clientRecordId = 19;
  const relatedAppId = kintone.app.getRelatedRecordsTargetAppId("案件一覧");
  const query =
    '顧客管理レコード番号_関連レコード紐付け用="' +
    clientRecordId +
    '" and 提案プラン in ("Aプラン")';
  const outputFields = ["合計費用"];
  const params = {
    app: relatedAppId,
    query: query,
    fields: outputFields,
  };
  console.log(params);

  // 集計値を取得する
  const client = new KintoneRestAPIClient();
  const res = await client.record.getRecords(params);
  console.log(res);

  // 合計金額集計
  const amount = res.records.reduce(
    (preV, p) => preV + parseFloat(p.合計費用.value),
    0
  );
  console.log(amount);

});
```

もう一度JavaScriptをupして動作確認します。
レコード番号19なら、集計値`amount`が1,500,000だったらOK。


##  🛠️PUT（updateRecord）でAプラン小計フィールドを更新する

特定の1件の集計までうまく行ったので、今度は特定の1件のフィールドを更新しましょう。

また私の記事ですが、ここからコードを流用します。

https://qiita.com/juri_don/items/138fa7c626da7d1590b4#updaterecord%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%891%E4%BB%B6%E6%9B%B4%E6%96%B0

このあたりを使います。

```js
  const rec = {
    社員コード: {
      value: obj.record.テーブル.value[0].value.社員コード.value,
    },
    氏名: { value: obj.record.テーブル.value[0].value.氏名.value },
    住所: { value: obj.record.テーブル.value[0].value.住所.value },
  };
  const res = await client.record.updateRecord({
    app: "名簿アプリのアプリID",
    id: text.value,
    record: rec,
  });
```

更新したいアプリIDは自分のアプリIDです。`kintone.app.getId()`でとってこれます。
※アプリID取得方法については、[アプリID取得](https://developer.cybozu.io/hc/ja/articles/202166300)に書いています。
レコードIDは引き続き19でベタ書きにします。

更新するのは `Aプラン小計フィールド` で、更新に使いたい値は先程集計した値の`amount`です。
それと、res という変数名は既に使っているのでres2とかにしておきましょう。
てことで、以下のように書き換えます。

更新が成功したら`console.log(res2);`で、
更新したレコードのrevision番号が表示されます。

```js
  const rec = {
    Aプラン小計: {
      value: amount,
    },
  };
  const res2 = await client.record.updateRecord({
    app: kintone.app.getId(),
    id: 19,
    record: rec,
  });

  console.log(res2);
```

ここまでで、ボタンクリック時のコードはこんな感じになるかと思います。

```js
// ボタンクリックした時
btn.addEventListener("click", async () => {
  // ボタンクリックした時のコード
  // 関連レコード集計用リクエストパラメータ準備
  const clientRecordId = 19;
  const relatedAppId = kintone.app.getRelatedRecordsTargetAppId("案件一覧");
  const query =
    '顧客管理レコード番号_関連レコード紐付け用="' +
    clientRecordId +
    '" and 提案プラン in ("Aプラン")';
  const outputFields = ["合計費用"];
  const params = {
    app: relatedAppId,
    query: query,
    fields: outputFields,
  };
  console.log(params);

  // 集計値を取得する
  const client = new KintoneRestAPIClient();
  const res = await client.record.getRecords(params);
  console.log(res);

  // 合計金額集計
  const amount = res.records.reduce(
    (preV, p) => preV + parseFloat(p.合計費用.value),
    0
  );

  // レコードを更新する
  const rec = {
    Aプラン小計: {
      value: amount,
    },
  };
  const res2 = await client.record.updateRecord({
    app: kintone.app.getId(),
    id: 19,
    record: rec,
  });
  console.log(res2);
});
```

JavaScriptをupしてボタンをクリックしてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/532929c6-3c03-d610-0d67-0b9e36f185f2.png)

revision:25って、何回更新してるんだって感じですが、アップデート無事にされたっぽいです。

しかし、一覧の方はこんな感じだったりするのではないでしょうか👀💦💦値が入っていない！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/f74df365-110b-9c96-f3d8-f53d37b12cc2.png)

画面表示を更新すると値が表示されます👀💦💦

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/d02db55c-978d-795a-a431-3795a2b4b9db.png)

なので、`console.log(res2);`の下辺りに
`location.reload();`を忍ばせておくとOKです。

というわけで、一覧表示後イベントのコードは特定の1件更新するだけでも
最終的にこうなりました。

```js
// 一覧でボタンをクリックしたら全件の「Aプラン小計」を更新する
kintone.events.on(["app.record.index.show"], (event) => {
  // ここに一覧表示後の処理を書く
  if (document.getElementById("👀") !== null) {
    return;
  }
  const sp = kintone.app.getHeaderMenuSpaceElement();
  const btn = new Kuc.Button({
    text: "ボタン",
    type: "submit",
    id: "👀",
  });
  sp?.appendChild(btn);
  // ボタンクリックした時
  btn.addEventListener("click", async () => {
    // ボタンクリックした時のコード
    // 関連レコード集計用リクエストパラメータ準備
    const clientRecordId = 19;
    const relatedAppId = kintone.app.getRelatedRecordsTargetAppId("案件一覧");
    const query =
      '顧客管理レコード番号_関連レコード紐付け用="' +
      clientRecordId +
      '" and 提案プラン in ("Aプラン")';
    const outputFields = ["合計費用"];
    const params = {
      app: relatedAppId,
      query: query,
      fields: outputFields,
    };
    console.log(params);

    // 集計値を取得する
    const client = new KintoneRestAPIClient();
    const res = await client.record.getRecords(params);
    console.log(res);

    // 合計金額集計
    const amount = res.records.reduce(
      (preV, p) => preV + parseFloat(p.合計費用.value),
      0
    );
    
    // レコードを更新する
    const rec = {
      Aプラン小計: {
        value: amount,
      },
    };
    const res2 = await client.record.updateRecord({
      app: kintone.app.getId(),
      id: 19,
      record: rec,
    });
    console.log(res2);

    // 画面表示更新
    location.reload();
  });
});
```

# 🙄今回の目標達成～

↓こんな感じになれば成功！次回は2～3件更新してみたいと思います。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/7ae78f80-1ae1-aee9-c61a-63036d403d31.gif" width="500px">