【kintone】別アプリのテーブルデータを追加、編集、削除する

## はじめに
kintoneのアプリで予約データ登録・編集時に、別アプリへデータを追加、編集、削除する仕組みを作成しました。
kintoneにはアプリアクションも存在していますが、更新は行えないため、自作しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700107/5772fea7-4831-24cd-f8e1-0673e6fb4e48.png)

## 予約管理アプリ
日付・ユーザー毎の予約を管理するアプリです。1日にユーザーが複数予約をする場合があり、その際は複数レコードに分けて登録を行います。

## 売上管理アプリ
予約日・ユーザー毎に1レコードとなる構成です。
予約管理アプリの1レコード分を売上管理アプリのテーブルに1行追加して、テーブルにてその日の売り上げリストを管理します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700107/4bf0e2bf-308b-9953-a7c0-608f6d52e65a.png)

### ユニークキー
予約日とユーザIDは一意となる必要があるため、2つの項目を組み合わせた値を保持します。予約管理アプリでの同時操作によって重複したデータが作成されることを防ぐためにユニークキーを持ちます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700107/6aebabd7-1248-e2ab-a475-bbea043d076c.png)
「値の重複を禁止する」に✔を入れることにより、設定可能です。
ユーザーには不要な項目のため、jsカスタマイズにより非表示に設定します。

### 姓、名
ユーザマスタをアプリを作成し、ユーザーIDをルックアップ項目に設定することによりデータを自動取得します。

### テーブル
予約管理アプリのレコード番号と作業費によって構成します。予約管理アプリからデータを登録する際に一致するレコード番号が存在するかどうかによって新規登録か更新かの判定を行います。レコード番号をルックアップ項目にすることにより、予約管理アプリからデータを送信する際にレコード番号から作業費の値を自動で取得することができます。これにより送信データを少なくすることができます。

## コード
```javascript
(function () {
  'use strict';

  // 売上管理アプリのアプリID
  const SALES_APP_ID = 1;

  /*
  * 保存成功イベント
  */
  kintone.events.on([
    'app.record.index.edit.submit.success',
    'app.record.create.submit.success',
    'app.record.edit.submit.success',
  ], async function (event) {
    const record = event.record;
    const userId = record.user_id.value;
    const reservationDate = record.reservation_date.value;
    const recordId = record.record_id.value;
    const newReservation = {
      'value': {
        'record_id': {
          'value': recordId,
          'type': 'DECIMAL'
        }
      }
    };

   // 売上管理レコード
    const salesManagementRecord = await getSalesManagementRecord(userId, reservationDate)

    // レコードが存在する場合は更新、無ければ新規追加する
    if (salesManagementRecord.totalCount > 0) {
      // レコードが存在する場合
      await UpdateSalesManagementRecord(recordId, salesManagementRecord, newReservation, 'PUT');
    } else {
      // レコードが存在しない場合
      await kintone.api(kintone.api.url("/k/v1/record", true),
        "POST",
        {
          app: SALES_APP_ID,
          record: {
            'unique_key': {
              'value': reservationDate + userId
            },
            'reservation_date': {
              'value': reservationDate
            },
            'user_id': {
              'value': userId
            },
            'table': {
              'value': [newReservation]
            }
          },
        });
    }
  });

  /*
  * レコード削除イベント
  */
  kintone.events.on([
    'app.record.index.delete.submit',
    'app.record.detail.delete.submit',
  ], async function (event) {
    const record = event.record;
    const userId = record.user_id.value;
    const reservationDate = record.reservation_date.value;
    const recordId = record.record_id.value;
    const newReservation = {};

    const salesManagementRecord = await getSalesManagementRecord(userId, reservationDate)

    await UpdateSalesManagementRecord(recordId, salesManagementRecord, newReservation, 'DELETE');

    return event;
  });

  /*
  * 売上管理アプリの対象のレコードを取得イベント
  */
  function getSalesManagementRecord(userId, reservationDate) {
    // 売上管理アプリからユーザーIDと予約日が一致するレコードを取得
    const salesManagementRecord = kintone.api(kintone.api.url("/k/v1/records", true),
      "GET",
      {
        app: SALES_APP_ID,
        query: 'user_id = "' + userId + '" and reservation_date = "' + reservationDate + '"',
        // totalCountを記述をしておくことで、データ取得後にtotalCountを用いた条件式を使用可能
        totalCount: true
      }
    );

    return salesManagementRecord;
  }

  /*
  * 売上管理アプリの対象のレコード更新イベント
  */
  function UpdateSalesManagementRecord(recordId, salesManagementRecord, newReservation, method) {
    const salesRecord = salesManagementRecord.records[0];
    const salesRecordId = salesRecord.id.value;
    const reservationList = salesRecord.table.value;
    // 存在チェック
    let isExist = false;

    for (let i in reservationList) {
      if (reservationList[i].value.record_id.value === recordId) {
        // 一致するレコード番号が存在する場合
        if (method === 'DELETE') {
          // レコード削除の場合、テーブルから行を削除する
          reservationList.splice(i);
        }

        // 一致する予約レコードが存在する場合
        isExist = true;
        break;
      }
    }

    if (!isExist) {
      // 一致する予約レコードが存在しない場合はテーブルに行を追加する
      reservationList.push(newReservation);
    }

    if (reservationList.length === 0) {
      // 売上管理のテーブルデータが0件の場合はレコードを削除
      kintone.api(kintone.api.url("/k/v1/records", true), 'DELETE',
        {
          app: SALES_APP_ID,
          ids: [salesRecordId],
        }
      )
    } else {
      // 売上管理のテーブルデータが存在する場合はレコードを更新
      kintone.api(kintone.api.url("/k/v1/record", true), 'PUT',
        {
          app: SALES_APP_ID,
          id: salesRecordId,
          record: {
            'table': {
              'value': reservationList
            }
          },
        }
      );
    }
  }

})();
```

## さいごに
上記お役に立てれば幸いです:blush:

## 参考
[kintoneでUPSERTしてみる](https://qiita.com/juri_don/items/7cb04e37c4b26bc6cf77)