キントーンで別アプリのレコード情報を現アプリへコピー

初めて質問をさせていただきます。
また、当方プログラム経験がVBAしかなくほぼ素人なため見当違いな質問をしておりましたら申し訳ありません。

前置きが長くなりましたが、今回行いたいことはアプリ間での情報転記となります。
別アプリに”QS-31-25-0001”のような番号が記載されている指示書のデータが蓄積されておりその値を現アプリから参照してその指示書の番号が含まれるレコード内の情報を現アプリのテーブル内文字列フィールドへ転記したいというものになります。
また、検索もテーブルの最初の文字列フィールドの値で行いたく、行が増えたら増えただけ検索を行いたいです。(最大5行としたいです)

併せて、QRコード読み取りプラグインでの入力でも情報の参照が行われてほしいのと、”25-0001”のような形で入力された場合には前後に”QS-31-”と”-T”をつけて特定のフォーマットにそろえる処理を行いたいです。
”0000”のようなかたちで入力が行われた場合には特に整形を行わないでそのまま入力をさせたい。

また、希望としてはこの動作をモバイル環境からでも行いたく、キントーンモバイルの環境下でも使えるようにしたいです。

実行したコードをコピー&ペーストしましょう

(() => {
    "use strict";
  
    const APP_ID_SOURCE = 19; // 検索先アプリのID
    const APP_ID_TARGET = 16; // 現アプリのID
    const TABLE_CODE = "作業リスト"; // 現アプリのテーブルフィールドコード
    const SEARCH_FIELD = "Management_number"; // 検索先アプリの検索対象フィールド
    const INPUT_FIELD = "指示書No"; // 現アプリの検索トリガーとなるフィールド
  
    // 転記するフィールドマッピング
    const FIELD_MAPPING = {
      "Construction_number": "工番",
      "Car_model": "車種",
      "Product_name": "品名",
      "Part_name": "部品名"
    };
  
    // PC・モバイル対応のイベント
    const events = [
      `app.record.create.change.${INPUT_FIELD}`,
      `app.record.edit.change.${INPUT_FIELD}`,
      `mobile.app.record.create.change.${INPUT_FIELD}`,
      `mobile.app.record.edit.change.${INPUT_FIELD}`
    ];
  
    kintone.events.on(events, async (event) => {
      console.log("イベント発火: ", event);
  
      const record = event.record;
      const table = record[TABLE_CODE]?.value;
  
      if (!table || !Array.isArray(table)) {
        console.warn("テーブルデータが取得できません。");
        return event;
      }
  
      console.log("作業リスト(テーブルデータ):", table);
  
      for (const row of table) {
        const rowData = row.value;
        const searchValue = rowData?.[INPUT_FIELD]?.value;
  
        if (!searchValue || searchValue.trim() === "") {
          console.warn(`行ID: ${row.id} - 指示書No が空なのでスキップ`);
          continue;
        }
  
        console.log(`検索値(指示書No): ${searchValue}`);
  
        const query = `${SEARCH_FIELD}="${searchValue}"`;
  
        try {
          // 他アプリから検索
          const resp = await kintone.api(kintone.api.url("/k/v1/records.json", true), "GET", {
            app: APP_ID_SOURCE,
            query: query
          });
  
          console.log(`検索結果 (${searchValue}):`, resp);
  
          if (resp.records.length > 0) {
            const result = resp.records[0];
  
            // **対象フィールドを初期化**
            for (const [sourceField, targetField] of Object.entries(FIELD_MAPPING)) {
              if (!rowData[targetField]) {
                rowData[targetField] = { type: "SINGLE_LINE_TEXT", value: "" };
              }
            }
  
            // **データ転記**
            for (const [sourceField, targetField] of Object.entries(FIELD_MAPPING)) {
              rowData[targetField].value = result[sourceField]?.value || "";
            }
          } else {
            console.warn(`検索結果なし: ${searchValue}`);
          }
        } catch (error) {
          console.error("検索エラー:", error);
          alert("データ取得時にエラーが発生しました。");
        }
      }
  
      return event;
    });
  
  })();

テーブルに転記したいのはレコードの詳細を開かなくてもレコード一覧から内容を確認したいためなので、関連レコードの表示では自分の行いたいことを満たせません。

ご確認のほどよろしくお願いします。

課題がいくつか混合しているので、切り分けをしたほうがいいかもしれません。

読んだ感じ、以下の別個の問題に分かれると思いますが、認識合っていますか?

  1. QRコード読み取りプラグインで入力するとき、特定の条件で入力値を変換したい
  2. テーブルに入力された作業指示書番号(or工番)をもとに、別のアプリからデータを取得したい
  3. テーブルの行数を最大5行に制限したい
  4. モバイル環境でもQRコード読み取りプラグインを使いたい
  5. モバイル環境でもカスタマイズを動かしたい

一度切り分けたうえで考えてみると、それぞれの問題に対して解決策が出てくると思います。


上記の内容が合っているとして、個人的に気になったところでいえば…

1.については、プラグインで設定したフィールドの変更時イベントで別のフィールドに変換した値を入力すればよいと思います。

2.については、コードを読む感じルックアップフィールドで対応可能なのでは?と思います。

3.については、テーブルの変更時イベントで6行以上あったら行削除を行えばよいと思います。

ご返信ありがとうございます。
自分でもいろいろ詰め込みすぎてわからなくなっている節があるので、切り分けは進めたいと思います。

1. QRコード読み取りプラグインで入力するとき、特定の条件で入力値を変換したい
→QRコードで入力する際には”QS-31-25-0001”のような正しいフォーマットで入力されるので、手入力で”25-0001”などと入力された際に整形を行いたいです。

2. テーブルに入力された作業指示書番号(or工番)をもとに、別のアプリからデータを取得したい
→作業指示書番号で検索を行いたいです。(工番では検索しない)

3. テーブルの行数を最大5行に制限したい
→その通りです。5行目までテーブルの行が増えていたら6行目は追加できないようにしたい。

4. モバイル環境でもQRコード読み取りプラグインを使いたい
→その通りです。(TISさんのQRコード読み取りプラグインを利用していますが、できています。)

5. モバイル環境でもカスタマイズを動かしたい
→その通りです。

const APP_ID_TARGET = 16; // 現アプリのID
          // **対象フィールドを初期化**
          for (const [sourceField, targetField] of Object.entries(FIELD_MAPPING)) {

APP_ID_TARGET と sourceField が宣言された後、読み込まれる機会がありません。
こういうのは Visual Studio Code を使えば一目で気づけます。
現アプリのIDは kintone.app.getId() で取得した方がいいです。

あと、コンソールにこんなエラーが出てないでしょうか?
Uncaught Error: app.record.create.change.指示書No is not allowed to return “Thenable” object.

「いいね!」 1

Uncaught Error: app.record.create.change.指示書No is not allowed to return “Thenable” object.

まさにその通りのエラーが出ています。

こんな風に、最後の return event;async を含む括弧の外に出すと
エラーを消せるようです。

    kintone.events.on(events, (event) => {
      (async () => {
        console.log("イベント発火: ", event);
    
        const record = event.record;
        const table = record[TABLE_CODE]?.value;
    
        if (!table || !Array.isArray(table)) {
          console.warn("テーブルデータが取得できません。");
          return;
        }
    
        console.log("作業リスト(テーブルデータ):", table);
    
        for (const row of table) {
          const rowData = row.value;
          const searchValue = rowData?.[INPUT_FIELD]?.value;
    
          if (!searchValue || searchValue.trim() === "") {
            console.warn(`行ID: ${row.id} - 指示書No が空なのでスキップ`);
            continue;
          }
    
          console.log(`検索値(指示書No): ${searchValue}`);
    
          const query = `${SEARCH_FIELD}="${searchValue}"`;
    
          try {
            // 他アプリから検索
            const resp = await kintone.api(kintone.api.url("/k/v1/records.json", true), "GET", {
              app: APP_ID_SOURCE,
              query: query
            });
    
            console.log(`検索結果 (${searchValue}):`, resp);
    
            if (resp.records.length > 0) {
              const result = resp.records[0];
    
              // **対象フィールドを初期化**
              for (const [sourceField, targetField] of Object.entries(FIELD_MAPPING)) {
                if (!rowData[targetField]) {
                  rowData[targetField] = { type: "SINGLE_LINE_TEXT", value: "" };
                }
              }
    
              // **データ転記**
              for (const [sourceField, targetField] of Object.entries(FIELD_MAPPING)) {
                rowData[targetField].value = result[sourceField]?.value || "";
              }
            } else {
              console.warn(`検索結果なし: ${searchValue}`);
            }
          } catch (error) {
            console.error("検索エラー:", error);
            alert("データ取得時にエラーが発生しました。");
          }
        }
      })(); // 非同期処理は即時関数でラップ
      return event;
    });
「いいね!」 2

ありがとうございます!
この修正を行ったところエラーが出なくなりそこを糸口に何とかPC上では正しい動きをするプログラムができました。

(() => {
  "use strict";

  const APP_ID_SOURCE = 19; // 検索先アプリのID
  const APP_ID_TARGET = 16; // 現アプリのID
  const TABLE_CODE = "作業リスト"; // 現アプリのテーブルフィールドコード
  const SEARCH_FIELD = "Management_number"; // 検索先アプリの検索対象フィールド
  const INPUT_FIELD = "指示書No"; // 現アプリの検索トリガーとなるフィールド

  // 転記するフィールドマッピング
  const FIELD_MAPPING = {
      "Construction_number": "工番",
      "Car_model": "車種",
      "Product_name": "品名",
      "Part_name": "部品名"
  };

  // PC・モバイル対応のイベント
  const events = [
      `app.record.create.change.${INPUT_FIELD}`,
      `app.record.edit.change.${INPUT_FIELD}`,
      `mobile.app.record.create.change.${INPUT_FIELD}`,
      `mobile.app.record.edit.change.${INPUT_FIELD}`
  ];

  kintone.events.on(events, (event) => {
      console.log("イベント発火: ", event);

      const record = event.record;
      const table = record[TABLE_CODE]?.value;

      if (!table || !Array.isArray(table)) {
          console.warn("テーブルデータが取得できません。");
          return event;
      }

      console.log("作業リスト(テーブルデータ):", table);

      // **すべての検索リクエストを実行**
      const requests = table.map((row) => {
          const rowData = row.value;
          const searchValue = rowData?.[INPUT_FIELD]?.value;

          if (!searchValue || searchValue.trim() === "") {
              console.warn(`行ID: ${row.id} - 指示書No が空なのでスキップ`);
              return Promise.resolve(null); // 空のPromiseを返す
          }

          console.log(`検索値(指示書No): ${searchValue}`);

          const query = `${SEARCH_FIELD}="${searchValue}"`;

          return kintone.api(kintone.api.url("/k/v1/records.json", true), "GET", {
              app: APP_ID_SOURCE,
              query: query
          }).then((resp) => {
              console.log(`検索結果 (${searchValue}):`, resp);
              return { rowData, result: resp.records[0] || null };
          }).catch((error) => {
              console.error("検索エラー:", error);
              return { rowData, result: null };
          });
      });

      // **すべてのリクエストが完了した後に処理**
      Promise.all(requests).then((results) => {
          results.forEach(({ rowData, result }) => {
              if (!result) return;

              // **対象フィールドを初期化 & 転記**
              for (const [sourceField, targetField] of Object.entries(FIELD_MAPPING)) {
                  rowData[targetField] = {
                      type: "SINGLE_LINE_TEXT",
                      value: result[sourceField]?.value ?? ""
                  };
              }
          });

          // ✅ 変更後のデータを event.record に反映
          event.record[TABLE_CODE].value = table;
          console.log("[DEBUG] 転記後の event.record:", event.record);

          // ✅ 最後に画面を更新
          kintone.app.record.set(event);
      });

      return event;
  });

})();

ただ、モバイル上では作動せずこの解決をしたいと思っています。
ここまで進めたのは非常に大きいと思っています。
本当にありがとうございます。

可能であれば引き続きご助言いただけないでしょうか?

kintone.app.record.set はモバイルだと表記が異なります。

スマートフォン用にアップロードするJavaScriptファイルでは
kintone.mobile.app.record.set と書かないと駄目です。

「いいね!」 1

本当に何から何までありがとうございました。
無事モバイルアプリでも正常に動きました。
本当に感謝しかありません。 :joy:

また何かあれば質問させてください。
この度はありがとうございました。