新規投稿
フォローする

ルックアップで添付ファイルフィールドをコピー

ルックアップの標準機能では、添付ファイルフィールドをコピーすることはできません。 JavaScriptカスタマイズにて、ルックアップ時に添付ファイルフィールドをコピーする方法を紹介します。

サンプル

フォーム設定

・アプリA(ルックアップコピー元)

・アプリB(ルックアップコピー先)

「ルックアップ」フィールドの設定の「ほかのフィールドのコピー」にて、「ルックアップ元アプリのレコード番号」を「ルックアップ先アプリのルックアップID」にコピーするよう設定します。

コード

js-sdkを利用しています。 アプリBで、下記「KintoneRestAPIClient.min.js」と「sample.js」を順に読み込みます。

・KintoneRestAPIClient.min.js

https://unpkg.com/@kintone/rest-api-client@1.1.0/umd/KintoneRestAPIClient.min.js

・sample.js

(function() {
  "use strict";
  var lookupField = 'ルックアップ';
  var lookupIdField = 'ルックアップID';
  var originAttachmentsField = '添付ファイル';
  var copyAttachmentsField = '添付ファイル';
  kintone.events.on([
    'app.record.detail.show',
    'app.record.create.show',
    'app.record.edit.show',
  ], function(event){
    kintone.app.record.setFieldShown(lookupIdField, false);
    event.record[copyAttachmentsField].disabled = true;
    return event;
  });
  kintone.events.on([
    'app.record.create.submit.success',
    'app.record.edit.submit.success',
  ], function(event){
    if(!event.record[lookupIdField].value) return;
    var client = new KintoneRestAPIClient();
    return client.record.getRecord({
      app: kintone.app.getLookupTargetAppId(lookupField),
      id: event.record[lookupIdField].value
    }).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(file){
        return client.file.uploadFile(file);
      }));
    }).then(function(copyFileInfomations){
      return client.record.updateRecord({
        app: event.appId,
        id: event.recordId,
        record: {
          [copyAttachmentsField]: {
            value: copyFileInfomations
          }
        }
      });
    }).then(function (){
      return event;
    });
  });
})();

※レコード編集画面でも、レコードを保存するたびに添付ファイルをコピーし直します。
※ゲストスペース内のアプリを扱う場合は、「new KintoneRestAPIClient()」の引数で「guestSpaceId」を指定する必要があります。
https://github.com/kintone/js-sdk/tree/master/packages/rest-api-client#usage

添付ファイルのコピーは、APIを4回程叩く必要があり、案外大変ですね。

 

※コード修正しました.(2021/02/18)
※コード修正しました.(2021/04/09)

2

24件のコメント

Avatar
SpLn

お世話になります。kintone初心者です。

アプリBの「ルックアップ」「ルックアップID」「添付ファイル」を全てテーブルに入れ、アプリAの「添付ファイル」をコピーできるアプリの作成を試みています。

しかしながら、下記のエラーを解決できておりません。

本記事のソースコードのどこを変更すれば実現できるようになりますでしょうか。
まったくのど素人で恐縮なのですが、ご教授いただけましたら幸いです。

1
Avatar
江田篤史

SpLn様

お世話になっております.

下記のようなコードになります.

(function() {
  "use strict";
  var table = 'テーブル'; //サブテーブルのフィールドコード
  var lookupField = 'ルックアップ';
  var lookupIdField = 'ルックアップID';
  var originAttachmentsField = '添付ファイル';
  var copyAttachmentsField = '添付ファイル';
  kintone.events.on([
    'app.record.detail.show',
    'app.record.create.show',
    'app.record.edit.show',
  ], function(event){
    kintone.app.record.setFieldShown(lookupIdField, false);
    event.record[table].value.forEach(function(row){
      row.value[copyAttachmentsField].disabled = true;
    });
    return event;
  });
  kintone.events.on([
    'app.record.create.submit.success',
    'app.record.edit.submit.success',
  ], function(event){
    var client = new KintoneRestAPIClient();
    return kintone.Promise.all(event.record[table].value.map(function(row){
      if(!row.value[lookupIdField].value) return [];
      return client.record.getRecord({
        app: kintone.app.getLookupTargetAppId(lookupField),
        id: row.value[lookupIdField].value
      }).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(copyFileInfomationsChunks){
      return client.record.updateRecord({
        app: event.appId,
        id: event.recordId,
        record: {
          [table]: {
            value: event.record[table].value.map(function(row, index){
              return {
                value: {
                  ...row.value,
                  [copyAttachmentsField]: {
                    value: copyFileInfomationsChunks[index]
                  }
                }
              }
            })
          }
        }
      });
    });
  });
})();
0
Avatar
辻川和伸

kintoneのアプリ間で添付ファイルコピーしたく、以下の投稿に従い設定しました。
https://developer.cybozu.io/hc/ja/community/posts/900001006786
ところがルックアップでレコード番号を入れてもサムネイル画像が表示されずコピーできません。カスタマイズ方法のどこに不備があるのか分かりません。アドバイスよろしくお願いします。なおhttps://unpkg.com/@kintone/rest-api-client@latest/umd/KintoneRestAPIClient.min.jsは、PC用のJavaScriptファイルurl欄とPC用のCSSファイルのurl欄両方に貼り付け保存の後、アプリを更新し、その後sample.jsをアップロードしアプリを更新しました。写真3枚を添付します。

 

辻川和伸により編集されました
0
Avatar
辻川和伸

江田篤史様、

江田様のsample.js(上のコード)を使いましたが、ルックアップでアプリAコピー元からアプリBコピー先に添付ファイルをコピーできませんでした。
試行錯誤の末、JSEdit for kintoneでsample.jsを見たところ21行目がエラー表示されていました。それで以下のように修正しました。

訂正前)var client = new KintoneRestAPIClient; 訂正後)var client = new KintoneRestAPIClient();

また、55行目の  });  もエラーになっていたので 55行目を削除しました。

この2ヵ所を修正したところ、アプリAコピー元から、アプリBコピー先に写真をコピーすることができました。

Javascriptの文法に詳しくないので、正確な処理だったのか確信は持てませんが、ご報告いたします。

この修正で問題ないでしょうか?

辻川

1
Avatar
江田篤史

辻川和伸さん

コメントありがとうございます。
仰る通り、コードにミスがありました。
申し訳ございません。

記事を修正したのでご確認いただければと思います。

0
Avatar
辻川和伸

江田様、

返信ありがとうございます。江田様の修正後のコードと、私のコードをdifffで比較したところ、

私のコードでは(JSEdit for kintoneにしたがい)、55行目(最後から2行目)の });を削除しましたが、

江田様の修正後のコードでは、52行目(最後から5行目)の })が削除されています。

文法的には、江田様の修正が正しいのですね。念のため再確認をお願いします。

辻川

 

0
Avatar
江田篤史

辻川和伸さん

お世話になっております。

2021/04/09現在の記事のコードが正しいです。
23行目のclient.record.getRecord()から始まるthen()チェーンつなげたいので、記事と同じ形が良いです。

JSEdit for kintoneでは、括弧の過不足のみで判断しているかと思います。
こちらでも動作はしますが、eventオブジェクトをreturnできないため、他のカスタマイズと競合する可能性があります。

0
Avatar
辻川和伸

江田様、

ご返信ありがとうございます。4/09現在の記事コードが正しいこと了解しました。

ルックアップで添付ファイルをアプリ間コピーできる今回のご投稿、現在、作成中のアプリで活用させていただきます。

添付ファイルコピーできることで、kintoneのルックアップがとても使い易いものになりました。
本当にありがとうございます。

1
Avatar
atouser

アプリ間の添付ファイルコピーを参考にさせて頂いております。

kintone初心者でお尋ねしたい事が御座います。

もし可能でしたら

アプリA(コピー元の添付ファイル)の部分がテーブル化されている場合の

どのような書き方をすれば宜しいでしょうか?

現在既存のアプリでテーブル化された添付ファイルを別アプリからルックアップでコピーしたいと

考えております。

宜しくお願い致します。

 

0
Avatar
hanazawa

カスタマイズ初心者の者です。
ルックアップ先から2つの添付ファイルをコピーしたいと考えております。
Promiseを勉強しつつやっているのですが、どこでthenを繋げばよいのかわかりません。
ご教授いただけますと幸いです。

  kintone.events.on(['app.record.create.submit.success','app.record.edit.submit.success',  ], function(event){
if(!event.record[lookupIdField].value) return;
var client = new KintoneRestAPIClient();

return client.record.getRecord({
app: kintone.app.getLookupTargetAppId(lookupField),
id: event.record[lookupIdField].value

}).then(function(originRecord){
//添付ファイル1つ目
return kintone.Promise.all([originRecord.record[originAttachmentsField1].value.map(function(originFileInfomation1){

return client.file.downloadFile({fileKey: originFileInfomation1.fileKey
}).then(function(fileData1){
return {
file: {
name: originFileInfomation1.name,
data: new Blob([fileData1], {type: originFileInfomation1.contentType})
}
};
});

//添付ファイル2つ目
}), originRecord.record[originAttachmentsField2].value.map(function(originFileInfomation2){
return client.file.downloadFile({fileKey: originFileInfomation2.fileKey
//
}).then(function(fileData2){
return {
file: {
name: originFileInfomation2.name,
data: new Blob([fileData2], {type: originFileInfomation2.contentType})
}
};
});

})]);

}).then(function(files){
return kintone.Promise.all(files.map(function(files){
return client.file.uploadFile(files);
}));

}).then([function(copyFileInfomations1){
return client.record.updateRecord({
app: event.appId,
id: event.recordId,
record: {[copyAttachmentsField1]: { value: copyFileInfomations1}}
});
}, function(copyFileInfomations2){
return client.record.updateRecord({
app: event.appId,
id: event.recordId,
record: {[copyAttachmentsField2]: { value: copyFileInfomations2}}
});
}]).then(function (){
return event;
});
});
0
Avatar
江田篤史

花澤龍生さん

お世話になっております。

Promise.all()を用いているのはよいと思います。

ただし、Promise.all()は複数のPromiseを1つのPromiseにまとめるだけです。
ご提示いただいたコードの45行目のように、thenメソッドの引数が関数の配列に変わるということはありません。

40行目のthenメソッドの引数として指定している関数(thenメソッドのコールバック関数)の引数を、記事と同様にfilesという変数名で扱っていますが、実際はfileの2次元配列になっているかと思います。

イメージとしては、下記のようなコードにするとよいと思います。

return client.record.getRecord({
  // ...
}).then(function (originRecord) {
  return kintone.Promise.all([
    //添付ファイル1つ目
    // ...
    ,
    //添付ファイル2つ目
    // ...
  ]);
}).then(function (filesChunks) {
  return kintone.Promise.all(filesChunks.map(function (files) {
    return kintone.Promise.all(files.map(function(file){
      return client.file.uploadFile(file);
    }));
  }));
}).then(function (copyFileInformationsChunks) {
  return client.record.updateRecord({
    app: event.appId,
    id: event.recordId,
    record: {
      [copyAttachmentsField1]: { value: copyFileInformationsChunks[0] },
      [copyAttachmentsField2]: { value: copyFileInformationsChunks[1] },
    }
  });
}).then(function () {
  return event;
});
0
Avatar
hanazawa

江田様

ご返信ありがとうございました。
複数の場合はPromise.all([1つ目、2つ目])とするのか、.then()でつなぐのか、どっちなのかと思っていましたが、
fileの部分はPromiseで入れ子になるのですね。
そしてclient.record.updateRecord部分は配列として2つファイルをアップロードさせてあげれば良いと。。。
まだ今の自分では考えつきませんでした。ご教示ありがとうございました。

恐縮ですが、追加でもう一つよろしいでしょうか。
ステータス変更時にルックアップデータからファイルコピー作業をしたいのですが、
kintone Rest APIエラー(400 Bad Request)が出てしまいました。
構造的には問題ないでしょうか。(returnの部分??)
なにか別の要因でしょうか?

kintone.events.on(["app.record.detail.process.proceed"], function(event){
var record = event.record;
var nStatus = event.nextStatus.value;
var client = new KintoneRestAPIClient();

// ステータスが「完了」の場合、添付ファイルコピー
switch(nStatus){
case "完了":

//以下、添付ファイル1つの場合で記載
return client.record.getRecord({
(中略)
}).then(function(originRecord){
(中略)
}).then(function(files){
(中略)
}).then(function(copyFileInformations){
(中略)
}).then(function (){
return event;
});
};
});

よろしくお願いいたします。

 

0
Avatar
江田篤史

花澤龍生さん

お世話になっております。

app.record.detail.process.proceedイベントでは、event.appIdとevent.recordIdがセットされていないのが原因かと思います。
https://developer.cybozu.io/hc/ja/articles/201941974#step3

代わりに、kintone.app.getId()kintone.app.record.getId()を用いるとよいかと思います。

江田篤史により編集されました
0
Avatar
hanazawa

江田様

お世話になっております。
基本的な間違いでお手数をおかけいたしました。

ご丁寧に返信いただきありがとうございました。

0
Avatar
Naoki_S

お世話になっております。

初心者となり基本的な質問となっておりましたら大変恐縮ですが、

コピー元にある添付ファイルをコピー先アプリへコピーしたく、上記手順に従い試しましたが

”保存”ボタンを1回押下すると何も反応せず、もう一度”保存”ボタンを押下すると以下のエラーが表示され

上手くできませんでした。

対処方法について教えて頂けますでしょうか。

※ゲストスペース内のアプリではないです。

 

参考1_コピー先アプリの設定状況

sample.jsの内容は丸ごとコピーして利用しています。

※コピーして利用できるよう、フィールド名、フィールドタイプ、フィールドコードを揃えています。

本運用の際に各自で更新する必要がある個所は以下と理解しております。

var lookupField = 'ルックアップ';
var lookupIdField = 'ルックアップID';
var originAttachmentsField = '添付ファイル';
var copyAttachmentsField = '添付ファイル';

参考2_コピー元アプリ_フォーム画面

 

参考3_コピー先アプリ_フォーム画面

※ルックアップ"取得"押下によりルックアップIDに数値が反映さることは確認しています。

以上、よろしくお願いいたします。

0
Avatar
江田篤史

Naoki_Sさん

お世話になっております。

下記を参考に、コンソール画面上になにかエラーが出ていないかご確認いただけますでしょうか。
https://developer.cybozu.io/hc/ja/articles/207613916

1
Avatar
Naoki_S

江田 様

お世話になっております。

早急なご回答ありがとうございます。

URLありがとうございました。

確認しましたところ以下のようなエラーを確認しました。

 

---

Refused to apply style from 'https://unpkg.com/@kintone/rest-api-client@1.1.0/umd/KintoneRestAPIClient.min.js' because its MIME type ('application/javascript') is not a supported stylesheet MIME type, and strict MIME checking is enabled.

---

とありますが、これは、KintoneRestAPIClient.min.js の設定が私のほうで上手くできていないということでしょうか。。。

不備などありましたらご指摘頂けますと幸いです。

お手数をお掛け致しますがよろしくお願いいたします。

0
Avatar
江田篤史

Naoki_Sさん

お世話になっております。

1, 2つ目のエラーについて
PC用のCSSファイルに「KintoneRestAPIClient.min.js」を設定しているかと思いますが、こちらの設定は不要になります。

3, 4つ目のエラーについて
sample.jsの13行目と20行目でエラーが出ているようですね。
event.record[copyAttachmentsField]とevent.record[lookupIdField]が未定義ということかと思います。
「テスト_コピー先」の「添付ファイル」フィールドと「ルックアップID」フィールドのフィールドコードが正しいか今一度ご確認いただけますか?

1
Avatar
Naoki_S

江田 様 早急なご回答ありがとうございます。

「テスト_コピー先」の「添付ファイル」フィールドと「ルックアップID」フィールドのフィールドコードは以下の通り設定しております。※フィールドコードに”空白”がないことは確認しております。

定義するために不足な点がありましたらご指摘頂けますと幸いです。

①sample.js上

---

var lookupField = 'ルックアップ';
var lookupIdField = 'ルックアップID';
var originAttachmentsField = '添付ファイル';
var copyAttachmentsField = '添付ファイル';

---

 

②コピー先「添付ファイル」フィールドのフィールドコード

 

③コピー先「ルックアップID」フィールドのフィールドコード

 

以上、よろしくお願いいたします。

0
Avatar
江田篤史

Naoki_Sさん

お世話になっております。

左様でしたか。

sample.jsの11行目と12行目の間辺りに下記を記述して、コンソール画面にどのように出力されるかご確認いただけますか。

console.log(event.record);

フィールド設定が正しければ、下記のように「ルックアップ」、「ルックアップID」、「添付ファイル」プロパティを持ったオブジェクトが出力されるかと思います。

江田篤史により編集されました
1
Avatar
Naoki_S

江田 様 早急なご回答ありがとうございます。

以下の様に確認しました。

お手数をお掛け致しますがよろしくお願いいたします。

 

0
Avatar
江田篤史

Naoki_Sさん

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

下記の出力もご確認いただけますでしょうか。

console.log(event.record[copyAttachmentsField]);
console.log(event.record[lookupIdField]);

もし上記の出力がundefinedになる場合は、sample.jsの文字コードがUTF-8となっているかご確認いただけますか?

1
Avatar
Naoki_S

江田 様 早急なご回答ありがとうございます。

ご推察の通り出力がundefinedとなりましたので、

文字コードの確認を行いましたところ無事できました。(UTF-8に変換)しました。

大変助かりました。また理解が深まりました。ありがとうございました。

1
Avatar
ICHIKI MASAYA

お世話になります。私もkintone初心者です。

こちらの記事にて、実施を試みたところ保存ボタンを押下しても反応しない事象が発生いたしました。

エラーは下記のみとなります。

Uncaught TypeError: Cannot read properties of undefined (reading 'value')
    at download.do?app=51&contentId=751&jsType=DESKTOP&hash=bcc45ee70c1a5a2481d38992ffe90ebf7c555936:20:37
    at edit.js:248:115
    at new rl (edit.js:120:57)
    at Tx (edit.js:248:94)
    at edit.js:247:402
    at e.o (edit.js:121:455)
    at Il (edit.js:123:476)
    at Dl (edit.js:123:359)
    at rl.N (edit.js:962:102)
    at kl (edit.js:119:407)

お手数おかけしますが、確認いただけますでしょうか。

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