新規投稿
フォローする

プロセス管理ステータス更新時におけるファイルアップロードについて

プロセス管理で承認完了時に上長印や社判を押すイメージで、
こちらの江田様の記事を参考に、プロセス管理のステータス更新時に画像ファイルアップロードをするアプリを作成しています。

ステータス変更時に、画像ファイルをアップロードすることはできているのですが、
ステータスが承認完了となった際、画像が表示されていないことが時々あります。
(画面更新すると表示される)

thenチェーンの終了方法を探ったり、
location.reloadやsetTimoutによる方法などいろいろ模索して試してみたのですが、
形を崩してしまうと私の力不足のため非同期処理がうまくできません。
どのようにしたら良いか分からず、お知恵をお借りできないでしょうか。

(function () {
  "use strict";
  var lookupField = 'ルックアップフィールド';
  var originAttachmentsField = '印影';
  var copyAttachmentsField = '社判';
              
  kintone.events.on(["app.record.detail.process.proceed"], function(event){
      var record = event.record;
      var nStatus = event.nextStatus.value;
      var client = new KintoneRestAPIClient();
   var StampParam = {app: kintone.app.getLookupTargetAppId(lookupField),  id: 1} ;


      // ステータスが「承認完了」の場合、押印する
      if(nStatus === "承認完了"){
                
                //押印画像添付
                return client.record.getRecord(StampParam).then(function(originRecord){
                    return kintone.Promise.all(originRecord.record[originAttachmentsField].value.map(function(originFileInfomation){
                        return client.file.downloadFile({fileKey:originFileInfomation.fileKey}).then(function(fileData){
                            return {
                                file: {
                                    name: originFileInfomation.name,   
                                    data: new Blob([fileData], {type: originFileInfomation.contentType})
                                }
                            };
                        });
                    }));
                }).then(function(files){
                    return kintone.Promise.all(files.map(function(files){
                        return client.file.uploadFile(files);
                    }));
                }).then(function(copyFileInfomations){
                    var copyParam ={
                            app: kintone.app.getId(),          
                            id: kintone.app.record.getId(),   
                            record: {
                                [copyAttachmentsField]: {
                                    value: copyFileInfomations
                                }
                            }
                    };
                     client.record.updateRecord(copyParam)});//thenチェーン終わり
                }//if文終了
            return event;
        });
    })();
0

12件のコメント

Avatar
新屋 育男

hanazawa 様

こんにちは。

ちゃんと中身確認してないので間違ってたら申し訳ないです。

このソースがしっかり意図通りに動いていて、かつthenを理解していることを前提でお答えしますね。

client.record.updateRecord(copyParam)

最後のこちらがresolveされたら、thenでつないでlocation.reloadを走らせたら大丈夫かと思いますよ。 

 

上の内容見て、理解していないとよくやる間違い。

.then(function(copyFileInfomations){
var copyParam ={
  app: kintone.app.getId(),        
  id: kintone.app.record.getId(),  
  record: {
    [copyAttachmentsField]: {
      value: copyFileInfomations
    }
  }
};
 client.record.updateRecord(copyParam) // ここが間違いポイント1
}).then(location.reload()); // ここが間違いポイント2

上の内容の問題点は2つです。

  • client.record.updateRecord(copyParam)がresolveされていない。必ずreturnをしてあげること。
  • thenがpromiseを受け取っていない。値を使わない時でも then(function () { 処理…}) と繋いであげることです。

ほとんどあとひと手間加えるだけでほぼ答えかもしれないですけど、理解していないとこれでも何を言っているのかわからないかもしれないです。

それでは参考になれば幸いでございます。

0
Avatar
hanazawa

新屋 育男 様

ご返信ありがとうございます。
上記2点について、修正したコードは下記であっていますでしょうか?

.then(function(copyFileInfomations){
                    var copyParam ={
                            app: kintone.app.getId(),          
                            id: kintone.app.record.getId(),   
                            record: {
                                [copyAttachmentsField]: {
                                    value: copyFileInfomations
                                }
                            }
                    };
                    return client.record.updateRecord(copyParam);
                }).then(function(){
                    location.reload();
                    return event;     
                });

こちらも一度試してみましたが、ファイルはアップロードされるものの、ステータスが変更されないのです。。。
プロセス管理でないイベントにおけるファイルアップロードでしたら、問題なく行くのですが、プロセス管理になるとうまくいきません。。。

hanazawaにより編集されました
0
Avatar
新屋 育男

hanazawa 様

ご確認ありがとうございます。

通常であれば修正はそれで大丈夫だと思います。

ただ、これはプロセスアクション実行前のイベントだったんですね。

簡潔にお伝えすると、実行後のイベントではない = 実行前にreloadしてるので、ステータスが更新されていない状態になってしまったということだと思います。

 

そうするとlocation.reloadを入れるタイミングが違いますね。

私も実験しないとわからないですけど、即時関数をasync functionにして、then で繋いで location.reloadするなんて言うのはどうでしょうか?

意味がわからなかったら言ってください。

実験してもらうためにコード書きます。

 

あっ、何が起きるか考えてたんですけどちょっと問題が起きそうなのでやめときます。

というか、一回自分で実験してみますね。

新屋 育男により編集されました
0
Avatar
hanazawa

新屋 育男 様

ご返信ありがとうございます。
上記コードのpromiseを全てasync/awaitで書き換える、ということかと思い、書き換えを数時間試みてみたのですが、
promiseのreturnが多く、またkintone.Promise.allなどもあり、混乱してしまい何が何だか・・・と言った感じになりました。
async/awaitを利用するのは一部分だけでもよいのでしょうか?
もしお分かりになりましたらご教授頂けますと幸いです。

0
Avatar
新屋 育男

hanazawa 様

いちよう検証終わりましたよ。

app.record.detail.process.proceed は、return event したときにステータスが更新されるみたいです。

※画面のreloadは走らない。

ですので、location.reload() のタイミングを return event の後に持ってくれば解決しました。

なので最後の部分はこんな感じでいけるみたいでした。

.then(function() {
return event;
}).then(function() {
location.reload();
});

想像よりも簡単に解決って感じですね。

勉強になりました。

0
Avatar
新屋 育男

ご質問に答えていなかったので、

> 上記コードのpromiseを全てasync/awaitで書き換える、ということかと思い、書き換えを数時間試みてみたのですが、
> promiseのreturnが多く、またkintone.Promise.allなどもあり、混乱してしまい何が何だか・・・と言った感じになりました。
> async/awaitを利用するのは一部分だけでもよいのでしょうか?

即時関数をasync function にするというのは、簡単にお伝えすると即時関数がthen(promise)を返すようにする。

ということですね。

どんな形になるかというと

(async function() {
...処理...
})()
.then(function() {
...処理...
})

みたいな形にするってことですね。

特にawaitとかは利用しないですよ。

 

ポイントは、async を入れるとthen(promise)を返すってことですね。

ただ、これにlocation.reloadを入れると永遠にreloadし続けると思い、『問題が起きそうなのでやめときます。』

と追記致しました。

まぁどちらにしてもあまり宜しくないコードだと思うので忘れてください。

0
Avatar
hanazawa

新屋 育男 様

お時間取ってご検証いただきありがとうございます。
新屋様の通り修正したのですが、
「レコードを再読込してください。他のユーザーがレコードを更新しました」
のエラーメッセージが出たり、エラーメッセージが途中まで出ているタイミングで画面表示が更新されており、解決に至っておりません。

なぜかreturn client.record.updateRecordのreturnを取り除くと、10回に9回ぐらい成功し、残りの1回で上記エラーメッセージが出て、
ステータスが変わらずファイルがアップロードされる、といったような具合です。
現時点でのコードは下記の通りです。

//上記省略
.then(function(copyFileInfomations){
                    var copyParam ={
                            app: kintone.app.getId(),          
                            id: kintone.app.record.getId(),   
                            record: {
                                [copyAttachmentsField]: {
                                    value: copyFileInfomations
                                }
                            }
                    };
  return client.record.updateRecord(copyParam);//ここのreturnを取り除くとうまくいくことが多い
}).then(function() {
  return event;
}).then(function() {
  location.reload();
});
0
Avatar
hanazawa

新屋 育男 様

回答の入れ違いすみません。
Promiseの書き換えではawait必須だと思っておりました。
今回は上手に処理できませんでしたが、今後asyncも勉強しつつ検証してみます。
async functionについてのご教授ありがとうございました。

0
Avatar
新屋 育男

hanazawa 様

確かに、

return client.record.updateRecord(copyParam);

でエラーが起きますね。

ミスを犯してました、検証してるときにreturnを入れるの忘れていました。

 

と言うことは、次のreturn event の時にはレコードの内容が更新(変更)されてるからエラーが起きているんですね。

とういことは、添付ファイルの更新より前に return event が必要みたいですね。

あらら、順番から結構作りなおさないといけなさそうですね。

簡単とか言っておきながら、ドツボにハマりましたね。(笑)

0
Avatar
hanazawa

新屋 育男 様

う~ん、8割方うまくいっている場合があり、残り2割が駄目なだけなので、
もう少しだとは思うのですが、、なかなか難しいものですね。。。
丁寧にご返信いただきありがとうございました。
もし引き続き良い方法がありましたらアドバイス頂けますと幸いです。
よろしくお願いいたします。

1
Avatar
新屋 育男

hanazawa 様

こんにちは。

色々検証した結果だけお伝えしますね。

app.record.detail.process.proceed でのイベントタイミングでのREST APIによる更新はほぼ絶望的でした。

 

理由ですが、たとえばプロセス管理系のアプリの場合、ある時点で編集不可にしたいなどの要望があったりすると思うのですが、その印影を添付ファイルに差し込む時に、イベントを発生させた時は編集権限があっても、その次のプロセスで編集権限が欠落していた場合などは全てエラーになります。

 

対応方法としては、

  • 自作でプロセスを制御するイベントを作成する。
  • APIトークン等を利用して、process.proceed が終了した後の detail.show でPOSTを実行する。

などが考えられます。

 

視点や方向性を変えれば技術的にはやりたいことを制御した形で実現することは可能ですが、process.proceedではほぼ完全に制御することは不可能でした。

 

あまりお力になれず、バグを残す形で終了してしまい大変申し訳ございません。

また、私の勉強になり大変有意義なお時間を頂きましたことお礼申し上げます。

 

元のデータに少し手を加えただけですが、

参考までに、process.proceed と detail.show で分離した場合に印影を制御するソースを置いておきます。

(function () {
  "use strict";
const lookupField = "ルックアップフィールド";
const originAttachmentsField = "印影";
  const copyAttachmentsField = "社判";
  let sign = false;

  kintone.events.on("app.record.detail.show", function (event) {
    if (!sign) return event;
    const client = new KintoneRestAPIClient();
    const StampParam = {
      app: kintone.app.getLookupTargetAppId(lookupField),
      id: 1,
    };
    return client.record
      .getRecord(StampParam)
      .then(function (originRecord) {
        return kintone.Promise.all(
          originRecord.record[originAttachmentsField].value.map(function (
            originFileInfomation
          ) {
            return client.file
              .downloadFile({ fileKey: originFileInfomation.fileKey })
              .then(function (fileData) {
                return {
                  file: {
                    name: originFileInfomation.name,
                    data: new Blob([fileData], {
                      type: originFileInfomation.contentType,
                    }),
                  },
                };
              });
          })
        );
      })
      .then(function (files) {
        return kintone.Promise.all(
          files.map(function (files) {
            return client.file.uploadFile(files);
          })
        );
      })
      .then(function (copyFileInfomations) {
        var copyParam = {
          app: kintone.app.getId(),
          id: kintone.app.record.getId(),
          record: {
            [copyAttachmentsField]: {
              value: copyFileInfomations,
            },
          },
        };
        return client.record.updateRecord(copyParam);
      })
      .then(function () {
        sign = !sign;
        return event;
      })
      .then(function () {
        location.reload();
      });
  });

  kintone.events.on("app.record.detail.process.proceed", function (event) {
    if (event.nextStatus.value === "承認完了") sign = true;
    return event;
  });
})();
0
Avatar
hanazawa

新屋 育男 様

返信が遅くなりすみません。
詳細にご確認いただきありがとうございました。
プロセス管理中にREST APIで操作するのは難しいのですね。。

対応方法として挙げていただいた、プロセス管理イベントを制御するは
ステータス更新APIの作成やボタン作成などでもう少し手間がかかりそうなので、
process.proceed が終了した後の detail.show でPOSTを実行する。
の方が実装が容易ですね。
運用問題ないか検討して、動きを検証してみたいと思います。

丁寧にご教授いただきましてありがとうございました。

0
サインインしてコメントを残してください。