アプリA内のテーブルを1行づつ、別のアプリB内のテーブルの該当行へ登録(集計)するには

こんにちは。いろいろ試したのですが、うまくいかないためこちらで相談させてください。

1. 想定の動作(図1)

「請求書アプリ」に請求情報(テーブル内の各行)を登録すると、「工事費用アプリ」のテーブルに集計表ができあがる。

このとき「工事費用アプリ」では「工事番号」ごとにレコードを作っています。

<図1>

 

2. 実際の動作(図2)

「請求書アプリ」に例えば4件(4行)分の請求情報を入力すると、「工事費用アプリ」側では 2件分しか集計されない。

再現性は90%程度で、残り10%は1件分しか(つまり、工事費用アプリ側で金額欄が1円となる)集計されない場合もあります。

3件以上集計できたことはありません。

<図2>

 

3. 説明用テスト入力(図3、図4)

説明を簡単にするため、「工事費用アプリ」にはあらかじめ「工事番号 001」「日付 2016-08-15」「支払先 A社」「金額 0円」を入力しておきました(図3)。「明細書」は「工事費用アプリ」内のテーブルのフィールドコードです。

<図3>

その次に、「請求書アプリ」に4件分登録してみました(図4)。「payTable」は請求書アプリ内のテーブルのフィールドコードです。

<図4>

 

4. ソースの説明

ソースを以下に示します。長くてすみません。これでもだいぶ簡略化して本質的な箇所を抜き出しています。

 

  1. (function() {
  2. “use strict”;
  3. //請求書アプリの操作
  4. var billApp = {};
  5. billApp.APP_ID = kintone.app.getId();
  6. billApp.APP_NAME = “請求書アプリ”;
  7. //工事費用アプリの操作
  8. var costApp = {};
  9. costApp.APP_ID = 73;
  10. costApp.APP_NAME = “工事費用アプリ”;
  11. //
  12. //請求書アプリのレコード追加画面(保存実行前)で、
  13. //工事費用アプリに情報を追加する
  14. //
  15. kintone.events.on(“app.record.create.submit”, function(e) {
  16. //追加しようとしているレコードのテーブル値取得
    
  17. var newTable = e.record['payTable'].value;
    
  18. billApp.insert(newTable).then(function(resp) {
    
  19.   if (resp === false) {
    
  20.     alert('\* NG \* 追加失敗');
    
  21.   }
    
  22.   return e;
    
  23. }).catch(function(resp) {
    
  24.   e.error = '\*\*\* NG \*\*\* ' + costApp.APP\_NAME + 'に登録できませんでした。';
    
  25.   return e;
    
  26. });
    
  27. });
  28. //--------------------------------------
  29. // 請求書アプリでの操作
  30. //--------------------------------------
  31. //
  32. //テーブル各行(請求情報)ごとに、
  33. //工事費用アプリへの登録する/しないを判定し、
  34. //登録する場合は登録処理を行う
  35. // @param array table 請求情報テーブル(payTable)
  36. // @param integer nextRow テーブルの行番号(初期値は0)
  37. // @return array/boolean [true, false,…]の配列(処理失敗時はarrayではなくfalseを返す)
  38. //
  39. billApp.insert = function(table, nextRow) {
  40. var row = nextRow || 0;
    
  41. // (工事番号が同じ)工事費用レコードを取得
    
  42. return costApp.get(table[row]['value']['工事番号'].value).then(function(resp) {
    
  43.   //
    
  44.   //工事費用アプリに、同じ工事番号のレコードが、既に存在するという条件下でのテスト
    
  45.   //
    
  46.   var costRecord = resp['records'];
    
  47.   //
    
  48.   //工事費用アプリに、同じ[日付, 支払先]の行が、既に存在するという条件下でのテスト
    
  49.   //
    
  50.   return costApp.increaseRow(costRecord, table[row].value, 0).then(function(resp4) {
    
  51.     //金額加算成功
    
  52.     //----- recursive processing -----
    
  53.     row++;  //1. 行を進める(次の支払情報へ)
    
  54.     if (row \< table.length) {  //2. 全ての行(支払情報)を処理したか
    
  55.       return billApp.insert(table, row);  //3. まだの場合、次の行(支払情報)の処理へ
    
  56.     }
    
  57.     return true;
    
  58.     //--------------------------------
    
  59.   }, function(resp4) {
    
  60.     //金額加算失敗
    
  61.     //----- recursive processing -----
    
  62.     row++;  //1. 行を進める(次の支払情報へ)
    
  63.     if (row \< table.length) {  //2. 全ての行(支払情報)を処理したか
    
  64.       return billApp.insert(table, row);  //3. まだの場合、次の行(支払情報)の処理へ
    
  65.     }
    
  66.     return false;
    
  67.     //--------------------------------
    
  68.   });
    
  69. }, function(resp) {
    
  70.   return false;  //存在確認不能
    
  71. });  //costApp.get
    
  72. }; //billApp.insert
  73. // -----------------------------------
  74. // 工事費用アプリへの操作
  75. // -----------------------------------
  76. //
  77. // [工事番号]を指定して、工事費用アプリのレコードを取得
  78. // @param object kojiNo 工事番号
  79. // @return Promise ‘GET’
  80. //
  81. costApp.get = function(kojiNo) {
  82. var params = {
    
  83.   // 工事費用アプリ番号
    
  84.   "app": costApp.APP\_ID,
    
  85.   // 検索条件
    
  86.   "query": '工事番号 ="' + kojiNo + '"',
    
  87.   // 取得項目
    
  88.   "fields": ['$id', '$revision', '工事番号', '明細書']
    
  89. };
    
  90. return kintone.api(kintone.api.url('/k/v1/records', true), 'GET', params);
    
  91. };
  92. //
  93. // テーブル内の1行を更新する(加算)
  94. // @param exsitedInf 工事費用アプリのレコード
  95. // @param newInf 請求書アプリの支払情報1件
  96. // @param rowIndex 更新する行を示す数(配列の添え字)
  97. // @return Promise
  98. //
  99. costApp.increaseRow = function(existedInf, newInf, rowIndex) {
  100. var costTable = existedInf[0]['明細書'].value;
    
  101. //支払金額を加算する
    
  102. var tmpValue = Number(costTable[rowIndex]['value']['金額'].value);
    
  103. costTable[rowIndex]['value']['金額'].value = tmpValue + Number(newInf['金額'].value);
    
  104. var params = {
    
  105.   "app": costApp.APP\_ID,
    
  106.   "id": existedInf[0]['$id'].value,
    
  107.   "revision": existedInf[0]['$revision'].value,
    
  108.   "record": {
    
  109.     "明細書": { "value": costTable }
    
  110.   }
    
  111. };
    
  112. // 更新
    
  113. return kintone.api(kintone.api.url('/k/v1/record', true), 'PUT', params);
    
  114. };
  115. })(); //----- function

「請求書アプリ」に4行分のデータを入力し、「保存」ボタンをクリックした時点以降を、GoogleChromeのディベロッパーツールで調べてみました。

ブレークポイントを適宜設定して実行してみますと、payTable 1行目の1円の行は正常に動作していました。しかし、payTable 2行目の10円の処理に入ったところで、(ソースコード42行目の)costApp.get関数がエラーを返し、ソースコード70行目のエラー処理にジャンプしてしまっていました。

 

(ソースコード81行目)costApp.get関数はシンプルな処理で、単に工事番号を受け取り、該当するレコードを「工事費用アプリ」から取得するだけのものです。

costApp.get関数がエラーを返す直前の処理でも、paramsの値は正常にセットされてていましたので、なぜ (ソースコード90行目の)return kinton.api(…); がエラーを返すのか理解できません。

 

請求書アプリの各行を処理する際に、Promiseを使い、再帰的に処理しているのですが、ここら辺でPromiseに関して私が何か誤解しているのかもしれません。

なお、GoogleChromeのディベロッパーツールでブレークポイント設置後に実行させると、payTable 1行目(1円の行)の処理は必ずうまくいくけども、payTable 2行目の10円の行は必ず失敗する、というように動作します。(この場合、工事費用アプリ側の(集計結果である)金額欄は 1円となります)

ブレークポイント無しで実行させ、リアルタイムで処理を流した場合は、たいてい payTable 2行目までは正常で payTable 3行目の100円の行で失敗します。(この場合、工事費用アプリの金額欄は 11円となります)

 

以上なのですが、何かヒントになるようなことでもよろしいですので教えていただけますでしょうか。よろしくお願いいたします。

 

 

すみません。テストでコメントの__書き込みしたんですが、消せないんですね…

うーん、ソースコードがインデントしないですね。タブと半角スペースでためましましたが、削除されて左寄せになってしまいます(T_T) 行番号も付かないし、、、

submitイベントにおけるkintone.Promiseのポイントであるkintone.Promise(のチェーン)をreturnするというところで、Promiseのチェーンが成り立ってないのが原因ではないでしょうか。

 

今回のケースですと、app.record.create.submitイベント中の

billApp.insert(newTable)

return billApp.insert(newTable)

とすると良いかと思います。

Ryu Yamashita さま

コメントいただきありがとうございます! ご指摘いただいた箇所、次の3行目のように return  を追記してみました。

  1. kintone.events.on(“app.record.create.submit”, function(e) {
  2. var newTable = e.record[‘payTable’].value;
  3. ** return** billApp.insert(newTable).then(function(resp) {
  4. if (resp === false) {
    
  5.   alert('\* NG \* 追加失敗');
    
  6. }
    
  7. return e;
    
  8. }).catch(function(resp) {
  9. e.error = '\*\*\* NG \*\*\* ' + costApp.APP\_NAME + 'に登録できませんでした。';
    
  10. return e;
    
  11. });
  12. });

すると、見事にうまく集計できるようになりました。

今一度、「レコード追加イベント」のドキュメントを読んでみて、「kintone.Promiseオブジェクトをreturnすることにより、非同期処理の実行を待ってからフィールドの値に応じた制御を実行することができます。」という説明文の意味をかみしめているところです。

本当にありがとうございました!

工事費用アプリのテーブルで「日付2016-08-15」となっているのは、同アプリであらかじめこのような日付で行を用意していたからです。今回お尋ねした件とは本質的に無関係です。一応、補足まで。

kintone.Promiseの使い方は、基本的にnative Promiseに習えば良いのですが、submitイベントへの対応はkintoneならではですね。

こちらで考察していますので、よろしければ合わせて。

https://www.joyzo.co.jp/blog/2021

ありがとうございます。大変ためになります!

Toshi Akazawaさん

>うーん、ソースコードがインデントしないですね。タブと半角スペースでためましましたが、削除されて左寄せになってしまいます(T_T) 行番号も付かないし

こちらはすでに解決されているようでしたね。
「コメントを書き込む」 ときのテキストエリアのメニュー左側にソースコードをそれっぽく表示できる機能があります。
更に言うとプログラム言語を解析してAceのように表示来たらと思います。解析は難しいでしょうけど…

うしロンさん、コメントありがとうございます。

折角ですので、元質問のソースコードもインデント付き、行番号付きで投稿し直しておきました。

空行は削除されちゃうようです。このため、元質問の行番号に言及している箇所が、今回のソースコードとずれてしまうことになってしまったので、元質問の行番号も編集して、つじつまが合うようにしてみました。

というわけで、勢いで「ソースコードをインデント付き、行番号付きで投稿するには」なんていう投稿もしてしまいました(^^;

このトピックはベストアンサーに選ばれた返信から 3 日が経過したので自動的にクローズされました。新たに返信することはできません。