別アプリのテーブルに追加更新

何を実現したいのかを書きましょう

出前弁当の発注をkintoneを利用して行いたいと思います
Aアプリでは個人個人が出前予約を書き込むアプリ
Bアプリでは出前発注担当者が出前業者に発注するためのアプリ
(出前発注担当者が出前業者に「うどんが8つで、カレーが5つで…」のように集計した結果をどこに配達するか出前業者にFAXを送るためです)
にしようと考えています

〇kintoneで2つのアプリを作成しました
A:1つは「弁当注文受付表」アプリ:アプリID342
B:1つは「弁当発注」アプリ:アプリID419

〇Aのアプリでのフィールド構成は
【レコード番号】、【日付】、【社員名】、【所属工場】、【弁当区分2】
という5つのフィールドから一つのレコードが構成されています
各個人がAアプリで5つの入力項目に入力してもらいます

〇Bのアプリでのフィールド構成は
【日付】、【第一工場の1弁当集計】、【第一工場の2弁当の集計】、【第二工場の1弁当集計】、【第二工場の2弁当集計】という5つのフィールドから一つのレコードが構成されています
【日付】以外のフィールドはテーブルから集計結果を算出するための計算フィールドです

〇Bのアプリではテーブルが存在しています
テーブルのフィールド構成は
Tテーブル:【レコード番号T】、【社員名T】、【所属工場T】、【弁当区分T】
4つのフィールドから一つのテーブルレコードが構成されています

〇このAとBの2つのアプリを連携させたいと思っています
出前発注担当が発注時にBアプリで新規追加ボタンを押すと動作が発動するようにしたいです

〇動作の流れは以下のようにしたいです
①、Bアプリで新規作成ボタンを押します
②、新規作成の詳細画面になります
③、Bアプリの【日付】フィールドにkintone標準機能で今日の日付が入るように設定してあります
④、保存ボタンを押します
⑤、保存を押したらAアプリの【日付】フィールド値とBアプリの【日付】フィールド値が一致するAアプリのレコードを取得します。この時のAアプリのクエリの条件がAアプリの【日付】フィールド値が今日から3日前までの範囲で検索します
⑥、⑤で取得したレコードを
・Aアプリの【レコード番号】フィールド値をBアプリTテーブルの【レコード番号T】フィールドにセット
・Aアプリの【社員名】フィールド値をBアプリTテーブルの【社員名T】フィールドにセット
・Aアプリの【所属工場】フィールド値をBアプリTテーブルの【レコード番号T】テーブルにセット
・Aアプリの【弁当区分2】フィールド値をBアプリTテーブルの【弁当区分T】テーブルにセット
⑦、画面をリロードする

〇Bアプリで詳細画面を開いて画面をリロードしたときに、Aアプリの【日付】フィールド値とBアプリの【日付】フィールド値が一致する条件のBアプリTテーブル内のレコードで、Aアプリにはレコードが存在していて、BアプリTテーブルにはレコードが存在しない場合、⑥と同じ条件でAアプリのレコードをBアプリTテーブルのレコードに更新追加する

〇Bアプリで詳細画面を開いて画面をリロードしたときに、Aアプリの【日付】フィールド値とBアプリの【日付】フィールド値が一致する条件のTテーブル内のレコードで、BアプリのTテーブルにはレコードが存在していて、Aアプリのレコードに存在しないレコードは、BアプリのTテーブルのレコードから削除する

発生した問題やエラーメッセージを具体的に書きましょう

エラーがでてしまい、たどっていくと"日付"フィールドに問題がありそうなのですが、Aアプリにはちゃんとフィールドコードが「日付」というものが存在しているのに、nullで返されてしまい困っています

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

(function() {
    "use strict";

    // AアプリのアプリケーションID
    var appAId = 342; // AアプリのアプリID

    // 保存ボタンが押されたときのイベントハンドラー
    kintone.events.on('app.record.create.submit', function(event) {
        // Bアプリの日付フィールドから日付を取得
        var bRecordDate = kintone.app.record.getFieldElement('日付').value;

        // Aアプリのクエリ条件を設定
        var threeDaysAgo = new Date();
        threeDaysAgo.setDate(threeDaysAgo.getDate() - 3);
        var threeDaysAgoString = threeDaysAgo.toISOString().split('T')[0];
        var query = '日付 > "' + threeDaysAgoString + '"';

        // Aアプリからレコードを取得
        kintone.api(kintone.api.url('/k/v1/records', true), 'GET', {
            app: appAId, // AアプリのアプリIDを使用
            query: query
        }, function(resp) {
            // BアプリのアプリID
            var appBId = 419; // 弁当発注アプリのアプリID

            // 検索結果のレコードを処理
            resp.records.forEach(function(record) {
                // BアプリのTテーブルにレコードを追加するデータを準備
                var postData = {
                    "app": appBId,
                    "records": [{
                        "レコード番号T": { "value": record.レコード番号.value },
                        "社員名T": { "value": record.社員名.value },
                        "所属工場T": { "value": record.所属工場.value },
                        "弁当区分T": { "value": record.弁当区分2.value }
                    }]
                };

                // BアプリのTテーブルにレコードを追加
                kintone.api(kintone.api.url('/k/v1/records', true), 'POST', postData, function(resp) {
                    console.log("Added record to B app successfully:", resp);
                });
            });
        });
    });

    // 詳細画面表示時のイベントハンドラー
    kintone.events.on('app.record.detail.show', function(event) {
        // Bアプリの日付フィールドの値を取得
        var bRecordDate = kintone.app.record.getFieldElement('日付').value;

        // Aアプリのクエリ条件を設定
        var query = '日付 = "' + bRecordDate + '"';

        // Aアプリからレコードを取得
        kintone.api(kintone.api.url('/k/v1/records', true), 'GET', {
            app: appAId, // AアプリのアプリIDを使用
            query: query
        }, function(resp) {
            // BアプリのアプリID
            var appBId = 419; // 弁当発注アプリのアプリID

            // 検索結果のレコードを処理
            resp.records.forEach(function(record) {
                // BアプリのTテーブルに存在しないレコードを削除する処理
                // ここに削除処理を記述
            });
        });
    });

})();

まだ全然知識がなく、うまくいくともあればうまくいかないこともあります。
知識のあるどなたかアドバイスいただきたいと思います

    // 詳細画面表示時のイベントハンドラー
    kintone.events.on('app.record.detail.show', function(event) {
        // Bアプリの日付フィールドの値を取得
        var bRecordDate = kintone.app.record.getFieldElement('日付').value;

        // Aアプリのクエリ条件を設定
        var query = '日付 = "' + bRecordDate + '"';

ここに問題がありそうですね。kintone.app.record.getFieldElement()は基本的にフィールドの装飾(色やサイズ等)を変えるためのもので、フィールドの中身(値)は取得できません。取得するのであればevent.record[‘フィールドコード’].valueという書き方になります(チュートリアルを読んでいけば理解できると思います)。

とはいえこれを直したら解決というわけではなく、後半(detail.show)は動作すると思いますが、前半(create.submit)は動作しないと思います。
レコードの保存時等でREST APIを含む非同期処理を行う場合、レコードの保存完了とページ推移が先に発生してしまうため、必要な処理の完了を待ってからレコードの保存をするような形にしなければなりません。
kintoneにおけるPromiseの基本が参考になると思います。

私も同じような機能を作ったことがあります(各社員が昼食を注文するアプリと、毎日決まった時間に自動で発注するアプリ)。応援しております。

橋本様
ご返信ありがとうございます。
コードを見直して作成しなおしています。
Aアプリのフィールドの中身を取得する動きは橋本様のアドバイスからできたのですが、Bアプリのテーブルに取得したフィールドをセットすることができず、うまくいかなくて行き詰っています。
そこでこのような記事がありました
関連レコードに表示される値を、(ボタンを押して)テーブルに挿入したい - kintone カスタマイズ - cybozu developer community
私もこの記事のようにボタンを押すと関連フィールド表示の値をテーブルに挿入してテーブルの集計値を計算フィールドにセットさえできてしまえば、発注までこぎつけられるのですが、記事の中のリンクがすべて切れてしまっていて、参考コードにたどり着けなくて困っています
私もこの記事と同様にボタンの作成までできるのですが、ボタンを押すと関連フィールドの値をテーブルに挿入するというコード作成ができないので、参考コードのリンクまたは参考記事がありましたら、ご紹介いただけたら幸いです

また、もしよろしければ「私も同じような機能を作ったことがあります」のコードを提供していただくことは可能でしょうか?提供いただけたらそのコードをベースにこちらのしたいことを置き換えられないかも検証したみたいと思っています。

大変恐縮ではございますが、お時間の許す限りで可能な限りでご教授いただきたいと思います。よろしくお願いいたします

miyawaki さま

Bアプリのテーブルに取得したデータをセットするには、create.submit(保存前イベント)だとREST APIを使用するのではなく直接event.recordを書き換える形にし、detail.showだとREST APIを使用する必要があり、コードを読む限りだとcreate.submitイベントでREST APIを使用しているのが原因ではないかと思います。

記事については私も分かりかねますが、江田さんが同様の機能として関連レコードの集計が簡単にできる機能を発表されていたので、そちらが参考になるかと思います。

私も同じような機能を作成しましたが、全く違うもの(注文アプリから発注アプリへデータを飛ばす形)なので参考にはならないでしょう。代わりに、私ならこんな感じで作るかなというものを参考程度に置いておきます。ここはそういったコミュニティではないのでコードをくれと言われても応じませんが、私も申し訳ありませんがmiyawakiさまが投稿された内容をあまり読み込んでいなかったので、その代わりと思って下さい。

動作テスト等は一切していないのでご承知下さい。あらかじめday.jsを読み込む必要があります。

(() => {
	let appId = 342;

	kintone.events.on(['app.record.detail.show', 'app.record.create.submit'], async (event) => {
		let record = event.record;
		let currentAppId = event.appId;
		let currentRecordId = event.recordId;
		let getRecordsBody = {
			app: appId,
			query: `日付 >= ${dayjs(record['日付'].value).subtract(3, 'day').format('YYYY-MM-DD')}`
		};

		let getRecords = await kintone.api(kintone.api.url('/k/v1/records', true), 'GET', getRecordsBody);
		let tableValue = gerRecords.records.map((getRecord) => {
				return {
					value: {
						レコード番号T: {
							value: getRecord['$id'].value
						},
						社員名T: {
							value: getRecord['社員名'].value // Aアプリの社員名フィールドがユーザー選択フィールドでBアプリの社員名Tフィールドが文字列フィールドならgetRecord['社員名'].value[0]?.name: ''
						},
						所属工場T: {
							value: getRecord['所属工場'].value
						},
						弁当区分T: {
							value: getRecord['弁当区分2'].value
						}
					}
				}
			});

		if (event.type.match(/submit/)) {
			record['Tテーブル'].value = tableValue;

			return event;
		} else {
			if (record['Tテーブル'].value.length !== getRecords.records.length) {
				let putRecordBody = {
					app: currentAppId,
					id: currentRecordId,
					record: {
						Tテーブル: {
							value: tableValue
						}
					}
				};

				await kintone.api(kintone.api.url('/k/v1/record', true), 'PUT', putRecordBody);

				location.reload();
			} else {
				return event;
			}
		}
	});
})();

橋本様
ご返答ありがとうございます
橋本様のコードをベースにして動かせないかとやってみようと思います
とりあえず何も考えないで提供いただいたコードを実装したみたら、
エラーがでまして、エラーの内容がday.jsにかかわるところのようです
edit.js:124 Uncaught ReferenceError: dayjs is not defined
at download.do?app=419&contentId=34579&jsType=DESKTOP&hash=161e180a7574ec00167174505c41b2b9663fa7ef:10:11
at edit.js:255:115
at new wk (edit.js:126:21)
at kx (edit.js:255:94)
at edit.js:254:425
at e.o (edit.js:127:405)
at Ok (edit.js:129:430)
at Jk (edit.js:129:313)
at wk.D (edit.js:867:102)
at pk (edit.js:125:365)
橋本様の「あらかじめday.jsを読み込む必要があります」が、初心者なので何をいっているのか私にはわからないのですが、day.jsにかかわるコードを別で実装しておく必要があるということでしょうか?
お手数ですがよろしくお願いいたします

miyawakiさま
day.jsはライブラリのことです。あらかじめ読み込んでおくことで複雑な処理を簡単に記述できるようになるものです(今回だと3日前の日付を簡単に求めるために使っています)。
ライブラリを使ってみようから辿れます。

橋本様
ありがとうございます
ライブラリの件理解できました
ライブラリday.jsを実装して再びコードを実装するとエラーがでました
/k/v1/records.json?a…E%3D%202024-04-28:1 Failed to load resource: the server responded with a status of 400 (Bad Request)

edit.js:124 Uncaught

  1. Object

  2. code: “CB_VA01”

  3. errors: {query: {…}}

  4. id: “HfoQzIYyXohvM9LhBvx4”

  5. message: “入力内容が正しくありません。”

  6. [[Prototype]]: Object
    何が悪いのかさっぱりわからないです
    申し訳ありません。お手数ですがご教授いただければ幸いです

クエリにダブルクォーテーションを忘れていました(ついでに不等号も)。以下のように変更して下さい。

			query: `日付 >= ${dayjs(record['日付'].value).subtract(3, 'day').format('YYYY-MM-DD')}`

			query: `日付 <= "${dayjs(record['日付'].value).subtract(3, 'day').format('YYYY-MM-DD')}"`

橋本様
ありがとうございます
今までよりも動きそうな感じです
コードを修正して実装したところ
コンソールではなくkintoneの標準画面上で赤枠白文字で下記のエラーメッセージがでました

  • カスタマイズ用のJavaScriptの実行時にエラーが発生しました。アプリの管理者にお問い合わせください。
    • event.record[‘Tテーブル’].value[0][‘レコード番号T’].type が不正です。
      以下同じようなメッセージがたくさんでてます

コンソールでエラーが出ないのでいい感じしますが、このメッセージは何を意味しているのか分かりますか?

miyawaki さま
フィールドのタイプ指定を失念しておりました。

					value: {
						レコード番号T: {
							value: getRecord['$id'].value
						},
						社員名T: {
							value: getRecord['社員名'].value // Aアプリの社員名フィールドがユーザー選択フィールドでBアプリの社員名Tフィールドが文字列フィールドならgetRecord['社員名'].value[0]?.name: ''
						},
						所属工場T: {
							value: getRecord['所属工場'].value
						},
						弁当区分T: {
							value: getRecord['弁当区分2'].value
						}

この部分にフィールドのタイプ(文字列フィールドか数値フィールドか)の指定が必要になります。文字列(1行)フィールドなら

					value: {
						レコード番号T: {
							value: getRecord['$id'].value
							type: 'SINGLE_LINE_TEXT'
						},

こうなります。フィールドの形式についてはフィールド形式を参考にして下さい。

橋本様
ご面倒見ていただき本当に助かっています
すべて文字列フィールドなのでタイプ指定は橋本様からアドバイスいただいた通りでOKだと思われます
'app.record.create.submit’ではエラーが出ていますが
'app.record.detail.show’ではテーブルにデータが挿入されるのは確認できました
惜しいところまで来ていると思います
クエリの取得検索範囲は今のものでいいのですが
実際に取得するレコードはその範囲すべてのレコードではなくて、
クエリの取得検索範囲の中から
Aアプリの【日付】とBアプリの【日付】が一致するレコードをテーブルに挿入したいと思っています
この場合はどのようにしたらいいでしょうか?
本当にお手数かけてます。すごく助かっています。

miyawaki さま
submitでエラーですか?コンソールには何か表示されていますか?

レコードの絞り込みは取得するレコードの絞り込み条件(クエリ)を変更するのが一番早いと思いますが、

			query: `日付 >= "${dayjs(record['日付'].value).subtract(3, 'day').format('YYYY-MM-DD')}"`

			query: `日付 = "${dayjs(record['日付'].value).format('YYYY-MM-DD')}"`

取得するレコードに意味があって取得後に絞り込みをする必要があるなら、filter()を使えば良いです。

		let tableValue = gerRecords.records.map((getRecord) => {

		let tableValue = gerRecords.records.filter((getRecord) => record['日付'].value === getRecord['日付'].value).map((getRecord) => {

橋本様
submitのエラーは
edit.js:124 Uncaught ReferenceError: gerRecords is not defined
at download.do?app=419&contentId=34679&jsType=DESKTOP&hash=250a4286835aaf5052b079a44d0fe3244706a9bc:14:20
で出ています。

今動作している状況は
Bアプリ新規追加でBアプリに今日の日付を入力して保存するとエラーがでます
新規追加ではなくてすでに今日の日付レコードが存在している状態で詳細画面に入ると、AアプリのすべてのレコードがBアプリのテーブルに挿入される動きになっています

filter()の部分がいけないのか、AアプリとBアプリの一致条件がうまくいっていないのかと思われます

miyawaki さま
おもいっきりタイポですね(gerRecords→getRecords)。失礼しました。

	let tableValue = gerRecords.records.map((getRecord) => {

	let tableValue = getRecords.records.map((getRecord) => {

橋本様
私も気づきました
ありがとうございます
橋本様のおかげで行き詰っていたのがだいぶ前進しています
もう少しなような気がします
詳細画面では動作が成り立っています

ただsubmit時のエラーがまだ解消されていません
基本的にはsubmit時で日付を入力して保存すると動作が成り立つようにしたいので、submit時で動作が成立すれば’app.record.detail.show’ではいらないのかな?とも思っています
コンソールにはエラーが出ていないのですが、kintoneのダイアログでエラーが出ています。
event.record[‘Tテーブル’].value[0][‘レコード番号T’].type が不正です。
・・・と
あとはフィールドのタイプ指定のところだと思います
橋本様にいただいたフィールド形式のコードにしてみると何も起こらなくなりました。
ちなみに
レコード番号はRECORD_NUMBER
社員名はルックアップフィールドなのでSINGLE_LINE_TEXT
所属工場は文字列フィールドなのでSINGLE_LINE_TEXT
弁当区分2は文字列フィールドなのでSINGLE_LINE_TEXT
だと思うのですが、何かわかりますかね?

橋本様
submit時に出現するエラーはこんな感じです

作り直してみました。試してみて下さい。

(() => {
	let appId = 342;

	kintone.events.on(['app.record.detail.show', 'app.record.create.submit'], async (event) => {
		let record = event.record;
		let currentAppId = event.appId;
		let currentRecordId = event.recordId;
		let getRecordsBody = {
			app: appId,
			query: `日付 <= "${dayjs(record['日付'].value).subtract(3, 'day').format('YYYY-MM-DD')}" and 日付 >= "${record['日付'].value}"`
		};

		let getRecords = await kintone.api(kintone.api.url('/k/v1/records', true), 'GET', getRecordsBody);
		let currentTable = record['Tテーブル'].value[0].value;
		let tableValue = getRecords.filter((getRecord) => record['日付'].value === getRecord['日付'].value).records.map((getRecord) => {
				return {
					value: {
						レコード番号T: {
							value: getRecord['$id'].value,
							type: currentTable['レコード番号T'].type
						},
						社員名T: {
							value: getRecord['社員名'].value,
							type: currentTable['社員名T'].type
						},
						所属工場T: {
							value: getRecord['所属工場'].value,
							type: currentTable['所属工場T'].type
						},
						弁当区分T: {
							value: getRecord['弁当区分2'].value,
							type: currentTable['弁当区分T'].type
						}
					}
				}
			});

		if (event.type.match(/submit/)) {
			record['Tテーブル'].value = tableValue;

			return event;
		} else {
			if (record['Tテーブル'].value.length !== getRecords.records.length) {
				let putRecordBody = {
					app: currentAppId,
					id: currentRecordId,
					record: {
						Tテーブル: {
							value: tableValue
						}
					}
				};

				await kintone.api(kintone.api.url('/k/v1/record', true), 'PUT', putRecordBody);

				alert('内容に変更があったためページを再読み込みします');
				location.reload();
			} else {
				return event;
			}
		}
	});
})();

detail.showの機能は個人的にはあっても良いかと思っています。あるかは分かりませんが追加の注文があった時、開いて保存しないと反映されないのと、開いただけで反映されるのとは違うと思います。

橋本様
いろいろとお手数をかけまして何とお礼を言ったらいいかわかりませんが
本当に助かっています。ありがとうございます
橋本様のコードを実装してみますと、
let tableValue = getRecords.filter((getRecord) => record[‘日付’].value === getRecord[‘日付’].value).records.map((getRecord) => {
の部分でエラーが起きているようです

type指定をしないときはdetail.showでなりたっていましたので、一度戻ってタイプ指定をしないコードに戻し私のほうでわからないまま少しコードを触ってみました
app.record.create.submit
の場合のみおかしかったのでそこを
app.record.create.change.日付
に直してコードを実装したところ、追加画面でもコードが動くようになりました。
これでとりあえずは弁当注文受付アプリから弁当発注アプリへの挿入はできたのですが、橋本様の言う通り追加注文があった場合の対応ができないです。
弁当発注アプリ上でレコードがテーブルに挿入された後、編集画面で追加でテーブルに一行追加して挿入して保存した場合、追加分が反映されずまた元に戻ってしまいます。あとそこだけを何とかしたいなぁと思っています
もし、橋本様の知見で何かわかることありましたらアドバイスいただきたいです。
とりあえず今動いているコードをお知らせします

(() => {
let appId = 342;

kintone.events.on(['app.record.detail.show', 'app.record.create.change.日付'], async (event) => {
	let record = event.record;
	let currentAppId = event.appId;
	let currentRecordId = event.recordId;
	let getRecordsBody = {
		app: appId,
		query: `日付 = "${dayjs(record['日付'].value).format('YYYY-MM-DD')}"`
	};

	let getRecords = await kintone.api(kintone.api.url('/k/v1/records', true), 'GET', getRecordsBody);
	let tableValue = getRecords.records.filter((getRecord) => record['日付'].value === getRecord['日付'].value).map((getRecord) => {
			return {
				value: {
					レコード番号T: {
						value: getRecord['$id'].value
					},
                    日付T: {
						value: getRecord['日付文字列'].value
					},
					社員名T: {
						value: getRecord['社員名'].value
					},
					所属工場T: {
						value: getRecord['所属工場'].value
					},
					弁当区分T: {
						value: getRecord['弁当区分2'].value
					}
				}
			}
		});

	if (event.type.match(/submit/)) {
		record['Tテーブル'].value = tableValue;

		return event;
	} else {
		if (record['Tテーブル'].value.length !== getRecords.records.length) {
			let putRecordBody = {
				app: currentAppId,
				id: currentRecordId,
				record: {
					Tテーブル: {
						value: tableValue
					}
				}
			};

			await kintone.api(kintone.api.url('/k/v1/record', true), 'PUT', putRecordBody);

			location.reload();
		} else {
			return event;
		}
	}
});

})();

miyawaki さま
またしてもタイポですね…。

let tableValue = getRecords.filter((getRecord) => record[‘日付’].value === getRecord[‘日付’].value).records.map((getRecord) => {

let tableValue = getRecords.records.filter((getRecord) => record[‘日付’].value === getRecord[‘日付’].value).map((getRecord) => {

手動で行を追加した場合はその編集を優先したいということでしょうか。このコードは「レコードの保存または閲覧時にアプリAのレコードを取得し、取得したレコードの数とBアプリで現在開いているレコードにあるサブテーブルの行数が違う場合」に動作するようになっています。
それであればサブテーブルにデータを反映する部分をconfirmで動作するようにして任意選択にさせる等の処理が簡単だと思いますが、完璧な対策とは言えないので運用で何とかする方向(手動操作用のサブテーブルを別で作成)が良いかもしれませんね。

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