サブテーブルにjsでセットすると計算フィールドが動作しない時がある

実現したいこと

サブテーブルにjsで値をセットした場合、テーブル内の集計を行う計算フィールドがうまく動作しないことがあります。計算処理を必ず動作させる方法はないでしょうか。

アプリの詳細

売上を集計するアプリを作成しています。
jsで生成したボタンを押すと、他アプリから売上データを取得し、サブテーブルにセットしています。
サブテーブル内の金額を計算フィールドを使用して合計値を表示しています。

実行したコード

button.addEventListener('click', async function() {

    // レコード情報を取得
    var record = kintone.app.record.get()

    // ------------------------
    // 売上データを取得する処理
    // ------------------------
    // クライアントの作成
    const client = new KintoneRestAPIClient();

    // リクエストパラメータの設定
    const params = {
        app: APP_ID,
        fields: fields,
        condition: query, 
        orderBy: orderBy,
    };

    // レコード取得
    const itemRecords = await client.record.getAllRecords(params);

    // サブテーブルを生成する処理
    subtable.push({
        value:{
            // 売上データ(itemRecords)から明細行を生成
        }
    });

    // サブテーブルにセット
    record.record.Table.value = subtable;

    // レコード情報をセット
    kintone.app.record.set(record); 

});

jsで集計する方法に変更できない諸事情

jsと計算フィールドの相性が悪いことは理解しております。
本来なら計算フィールドから数値フィールドに変更し、js側でそのまま合計値を集計させれば済む話なのですが、以下の2点の理由からそれが難しい(というより、できればあまりやりたくない)という状況です。

  • 既にかなりの数のアプリで同様の処理を行っており、運用していること
  • その計算フィールドを参照して、さらに別の計算を行っていること
    (例)[計算フィールドA]+[計算フィールドB]+[数値入力フィールド]+[計算フィールドC]

googleやこのコミュニティで、数日調査してみたのですが、なかなか解決策が見つからず、こちらに投稿させていただきました次第です。
何か足りない情報がありましたら、すぐに追加させていただきます。
どうぞよろしくお願いいたします。

日本語を勉強中ラスのと申します。

以下のような動作でしょうか。

click

「いいね!」 1

Irasさん

返信が遅れてしまいすみません。
はじめまして。返信ありがとうございます。
そしてご丁寧にテストコードまで作ってくださりありがとうございます!
基本動作はその動画とコードの通りです。

【問題の動作について】
その中でいうところの「計算」のフィールドが、たいていの場合はきちんと動作してくれる(自動計算してくれる)のですが、テーブルには新しい行がセットされているのに、合計値だけが再計算してくれない(計算フィールドが空の状態)という現象が時々起こります。
ちなみに、空だった場合、再度ボタンを押すときちんと再計算してくれます。

おそらくkintone側の計算フィールドの処理とkintone.app.record.set()が動く処理のタイミングがずれた場合に起こっているのだと思います。

こんにちは。
こんな感じでthenの中に入れて処理を待ってからkintone.app.record.set()を行うのはどうでしょうか?
パッと思いついただけの未検証なコードで恐縮ですが、、、


    // レコード取得
    await client.record.getAllRecords(params)
    .then((itemRecords) => {
        // サブテーブルを生成する処理
        subtable.push({
            value:{
                // 売上データ(itemRecords)から明細行を生成
            }
        });

        // サブテーブルにセット
        record.record.Table.value = subtable;

    });

    // レコード情報をセット
    kintone.app.record.set(record); 


「いいね!」 1

t.noriさん

返信ありがとうございます!
ご連絡が遅くなってしまいすみませんでした。

コードのご提供ありがとうございます。大変助かります。
ぜひ試してみたいと思うのですが、thenの中で処理する場合と、awaitのみで処理する場合とは、同期処理のタイミングが異なる、ということでしょうか?

私も人様にレクチャーできるほど熟達しているわけではないのですが、
非同期が基本の仕組みであるjavascriptで、同期処理を行ってくれる関数だと理解しています。

@deMae 様がコメント内で

と話していましたので、この辺りが改善策になるかと考え提案しました。

今回の例ですと、

const itemRecords = await client.record.getAllRecords(params);

の箇所だけiremRecordsに値が返却されるまで処理を待ってくれているが、

後続の

// サブテーブルにセット
record.record.Table.value = subtable;

の処理が完了している・していないに関わらず

// レコード情報をセット
kintone.app.record.set(record); 

の処理に進んでいるのではないかと考えています。

ご参考までに、私が同期処理の勉強で参考にしていたページを貼りますね。

「いいね!」 1

t.noriさん

丁寧に解説、また参考サイトの記載までありがとうございます。

つまり、本来であれば、

const itemRecords = await client.record.getAllRecords(params); // 1

record.record.Table.value = subtable; // 2

kintone.app.record.set(record); // 3

// 4:計算フィールドの自動計算処理

という順番で動いてほしいのが、

const itemRecords = await client.record.getAllRecords(params); // 1

record.record.Table.value = subtable; // 2 処理中・・・

kintone.app.record.set(record); // 3 処理中・・・ 

// 4:計算フィールドの自動計算処理

// 2完了
// 3完了

という順番になっているために、起こっている現象かもしれない、ということで合っておりますでしょうか?

いいえ、自動計算完了タイミングが”kintone.app.record.set(record);”より遅れる事があるのが、計算が反映されない原因ではないか?と感じた次第です。

引用させていただきますと以下の流れです。

const itemRecords = await client.record.getAllRecords(params); // 1

record.record.Table.value = subtable; // 2

// 3:計算フィールドの自動計算処理

kintone.app.record.set(record); // 4

// 3完了

横ですみません。

ざっくりで試したところ、以下の順番でした。

  • — レコード編集画面を表示した後のイベント

  • ボタンを追加します。

  • ボタンにクリックイベントを追加します。

  • — ボタンをクリックしました。

  • 現在のレコードを取得します。

  • 現在のレコードを取得しました。

  • 別レコードを取得します。

  • 別レコードを取得しました。

  • テーブルを生成する。

  • 反映させる 。

  • — 「数値」フィールドは更新されました (その後、計算される想定)。

コード

(function() {
  'use strict';
  const APP_ID = kintone.app.getId();
  
  //  クリックのコールバックを定義します。@deMase さん のコードのように、addEventListenerのコールバックに直接書くのも動作は同じです。
  const onClickHandler = async function () {
      
      console.log('---ボタンをクリックしました');
      
      console.log('現在のレコードを取得します');
      const currentRec = kintone.app.record.get();
      console.log('現在のレコードを取得しました', currentRec);
      
      console.log('別レコードを取得します。');
      // テストのため、同アプリ。別アプリでも可
      const params = {
        app: APP_ID,
      };
      const client = new KintoneRestAPIClient();
      const {records: otherRecords} = await client.record.getRecords(params);
      console.log('別レコードを取得しました。', otherRecords);

      console.log('テーブルを生成する')
      // テストのため、処理をシンプル化。
      otherRecords.forEach((otherRecordItem) => {
        const newRows = otherRecordItem.テーブル.value
          .map((row) => ({
            id: '',
            value: {
              数値: {
                type: 'NUMBER',
                value: Number(row.value.数値.value) * 2},
              数値_0: {
                type: 'NUMBER',
                value: Number(row.value.数値_0.value) * 3
              },
              計算_row: {
                type: "CALC",
                value: "" // 自動計算されるか、試しに空
              }
            }
          }))
        
        currentRec.record.テーブル.value.push(
          ...newRows
        )
        
      });
      
      console.log('反映させる', currentRec);
      kintone.app.record.set(currentRec); 
  }
  
  
  kintone.events.on('app.record.edit.show', function() {
    console.log('--- レコード編集画面を表示した後イベント');
    
    console.log('ボタンを追加します。');
    const btnContainerEl = kintone.app.record.getSpaceElement("button");
    const button = document.createElement('button');
    button.textContent = "クリック";
    btnContainerEl.appendChild(button);
    
    console.log('ボタンにクリックイベントを追加します');
    button.addEventListener('click', onClickHandler);
    
  });
  

   // 以下はイベントの順を表すためのもので、処理はありません。削除しても、動作は変わりません。
  kintone.events.on('app.record.edit.change.数値', function() {
    console.log('---「数値」フィールドは更新されました ');
    // テーブル内の数値が変更後、計算される。
  });
})();

動作

clickevents