質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

Q&A

解決済

1回答

1598閲覧

ReferenceErrorになってしまう…

10-mo

総合スコア23

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

0グッド

0クリップ

投稿2023/09/02 00:26

編集2023/09/02 00:48

実現したいこと

ここに実現したいことを箇条書きで書いてください。
-Googleフォームから申し込みされたイベント予約をスプレッドシートに集計し、スプレッドシートのGASから予約可否等のメール送信をしたい。

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

前提

/ここに質問の内容を詳しく書いてください。
イベントの予約をするためのツールを作成しています。Googleフォームで予約申し込み→スプレッドシートで集計し、スプレッドシートのGASから予約可否・QRコードの発行とメール送信をしたいと考えています。

発生している問題・エラーメッセージ

エラーメッセージ ReferenceError: target is not defined ### 該当のソースコード function form(e) {//メイン関数 var values = e.values;//フォームの回答を取得 //var stamp = values[0];//タイムスタンプを取得 var mail = values[1];//アドレス var date = values[2];//日付 var name = values[3];//氏名 var child = values[4];//お連れ様の人数 var way = values[5];//イベント知ったツール var up = 'FALSE'; var brank = ''; var ss = SpreadsheetApp.getActiveSpreadsheet();//スプレッドシートを有効にする var sht = ss.getSheetByName('来場者'); sht.appendRow([brank, mail, name, way, child, up, date]); var today = Utilities.formatDate(new Date(), "JST", "yyyy/MM/dd"); var ss = SpreadsheetApp.getActiveSpreadsheet(); var sht = ss.getSheetByName("来場者"); var lastrow = sht.getLastRow(); // 最終行を取得 var lastcol = sht.getLastColumn(); // 最終列を取得 var range = sht.getRange(1, 1, lastrow, lastcol); var values = range.getValues(); // 情報をオンメモリに保持 for (var i = 1; i < lastrow; i++) { data = values[i][0]; if (data == "") { values[i][0] = getRndStr(); } values[i][8] = false; var qrc1 = '=image(\"https://chart.apis.google.com/chart?chs=250x250&cht=qr&chl=\"&A' + (i + 1) + ')'; values[i][7] = qrc1; } range.setValues(values); //スプレッドシートに書き戻し for (let row = 1; row <= lastrow; row++) { if (sht.getRange(row,3).getValue() == name) { var id = sht.getRange(row,1).getValue(); } } //------------------------------------------ // 日程シートから該当日のデータを取得 // フォームの送信内容を取得 const items = e.response.getItemResponses(); const reserveDate = items[2].getResponse(); // 予約処理 const result = updateDatesSheet(reserveDate); if (result === "success"){ reserveSheet.appendRow([email, reserveDate]); // 予約シートに追記 updateForm(); // フォームの「空き状況」を更新 } function updateDatesSheet(reserveDate){ const ss1 = SpreadsheetApp.openById("シートID入れてます"); const datesSheet = ss1.getSheetByName("日程"); const table = datesSheet.getDataRange().getValues(); const index = table.findIndex(row => row[0] === reserveDate); const target = { rowNum: index + 1, date: table[index][0], cap: table[index][1],//日程シートB列 reserved: table[index][3],//日程シートのD列(人数シートから集計した人数) } } // 定員超過しなければ「予約済」に加算する if (target.reserved < target.cap) { datesSheet.getRange(target.rowNum, 3); return "success" } else { return "error" } } function sendEmail(mail, preferredDate, result){ const mailTitle = "予約結果"; const imageurl = 'https://chart.apis.google.com/chart?chs=250x250&cht=qr&chl=' + id; const response = UrlFetchApp.fetch(imageurl, option); const blob = response.getBlob().getAs(MimeType.JPEG); const mailBody = (result === "success") ? "予約が完了しました。\n 添付されたQRコードを当日の受付でご提示ください \n" +` 【予約日】:${preferredDate}` + '【氏 名】' + name + '\n' + '【お申込み人数】' + child + '名\n' + '【来場者ID】' + id : "定員超過のため予約できませんでした。\n" + "下記のフォームから再度申請してください\n" + form.getPublishedUrl(); const option = { method: "get", "attachments": blob, 'name' : 'イベント', } GmailApp.sendEmail(mail, mailTitle, mailBody, option); } function getRndStr() { var str = "abcdefghijklmnopqrstuvwxyz0123456789"; var len = 8; var result = ""; for (var i = 0; i < len; i++) { result += str.charAt(Math.floor(Math.random() * str.length)); } return result; } ### 試したこと targer前後の「{ 」の場所を変えてみましたが、そうするとindexやtableが白文字になってしまいました。配置する場所はここしかないような気がするので大きな変更はしていません。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

質問文のエラーについては、updateDatesSheetの中括弧の位置 がおかしいことが原因です。
このエラー自体は、updateDatesSheet関数の続きのif文をupdateDatesSheet関数に含めるとともに
修正後のupdateDatesSheet関数をform関数の外に出すことで解消されます。

しかし他にも大量のバグが残っていてエラーが出るため、下記に原因と対応を記載します。

(注意:以下は、「2023/09/02 14:01のコメント内で共有されたスプレッドシート」の中に記載された、その時点でのコードに対する言及です。以降にコードが編集されていても関知しません。(関知できません))

1.「var id = sht.getRange(row, 1).getValue(); //ここのid部分が反応しなくなってしまった。」の原因

→ スコープ内で、変数idがどこからも参照されていないからです。
変数id は、sendEmail関数内で使われているため、sendEmail関数を呼ぶときに、idも一緒に引数として渡してやればよいです。
(idだけでなく、 name, childも sendEmail関数内で使われているため、idと一緒に引数として渡してやる必要があります)

2.「TypeError: Cannot read properties of undefined (reading 'getItemResponses') at form(コード:44:28)」 の原因

→ フォーム入力内容を取得する書き方として、getItemResponses関数は、フォームのコンテナバインドスクリプトとして書いたときだけ有効です。

質問者さんによれば、スプレッドシートのコンテナバインドスクリプトとして書かれているとのことですので、getItemResponsesは使えません。
代わりに、form関数の冒頭の「var values = e.values」のように、valuesプロパティを使いましょう。

ただ、reserveDateに格納しようとしているのは、フォームで入力された予約日ですが、予約日は、form関数の最初の方の

var date = values[2];//日付

ですでにdate変数として取得済みです。したがって、改めて

var reserveDate = values[2];

とする必要はありません。date変数をそのまま使いまわせばいいです。

その他
3:reserveSheetの未定義エラー

reserveSheet がどこにも定義されていないため、このままだとエラーになります。

4:予約数更新処理のバグ

日程シートの予約数を更新する下記の部分

datesSheet.getRange(target.rowNum, 3)

ですが、入力範囲を取得しているだけで、値が書き込まれていません。
値を書き込むには、「setValue(<値>)」を追加する必要があります。

5. 日付が見つからなかった時のエラー処理

フォームの回答で日付を選択していますが、万が一、選択肢の日付がスプレッドシート上になかったり、フォームの質問とスプレッドシートのデータの間でコピぺミス等で文字不一致になっている場合、現状のコードだと updateDatesSheet の

const index = table.findIndex(row => row[0] === reserveDate);

で indexが-1になり、後続の処理でエラーになります。
したがって、きちんとエラートラップを置くべきです。
具体的には、上記の行の下に

if (index === -1) return "error";

を追加します。

6. 定員超過判定のバグ

現状、updateDatesSheet関数の「 定員超過しなければ「予約済」に加算する」の中で、定員超過の判定処理として

if (target.reserved < target.cap) {

としていますが、これだと、更新前の予約人数と定員を比べていることになるため、常にtrueになり、実際は超過する場合でも予約できてしまいます。
ここは、[更新前予約人数+フォームで回答された予約人数]と定員とを比較すべきです。
このためには、呼び出し元のform関数でupdateDatesSheet関数を呼び出す際、updateDatesSheet関数に、フォームから回答された予約人数の情報を渡してやる必要があります。

7. form.getPublishedUrl()がエラーになる。

sendEmail関数の中で、

js

1 const mailBody = (result === "success") 2 ? "予約が完了しました。\n 添付されたQRコードを当日の受付でご提示ください \n" 3 + ` 【予約日】:${preferredDate}` 4 + '【氏 名】' + name + '\n' 5 + '【お申込み人数】' + child + '名\n' 6 + '【来場者ID】' + id 7 : "定員超過のため予約できませんでした。\n" + 8 "下記のフォームから再度申請してください\n" + 9 form.getPublishedUrl();

としていますが

質問中のコードの「form」は関数でありフォームオブジェクトではないため、getPublishedUrl関数を持っていません。
定員超過時に form.getPublishedUrl() が呼び出されるとエラーになります。
ここはフォームのURLを直接記述する必要があります。


修正後の全体コード

上記を踏まえて修正したものが下記です。(一部の要件が不明確のため、期待する動作になっていない部分があるかもしれませんがあしからず。)
注意:updateForm関数は、エラー回避のためダミーで作っていますが、実際は具体的な処理に置き換えてください。

js

1function form(e) {//メイン関数 2 var values = e.values;//フォームの回答を取得 3 //var stamp = values[0];//タイムスタンプを取得 4 var mail = values[1];//アドレス 5 var date = values[2];//日付 6 var name = values[3];//氏名 7 var child = values[4];//お連れ様の人数 8 var way = values[5];//イベント知ったツール 9 var up = 'FALSE'; 10 var brank = ''; 11 var ss = SpreadsheetApp.getActiveSpreadsheet();//スプレッドシートを有効にする 12 var sht = ss.getSheetByName('来場者'); 13 sht.appendRow([brank, mail, name, way, child, up, date]); 14 15 var today = Utilities.formatDate(new Date(), "JST", "yyyy/MM/dd"); 16 var ss = SpreadsheetApp.getActiveSpreadsheet(); 17 var sht = ss.getSheetByName("来場者"); 18 var lastrow = sht.getLastRow(); // 最終行を取得 19 var lastcol = sht.getLastColumn(); // 最終列を取得 20 var range = sht.getRange(1, 1, lastrow, lastcol); 21 var values = range.getValues(); // 情報をオンメモリに保持 22 23 /*来場者シートのデータを全部取得し、1行ずつチェック。 24 A列が空欄の場合はランダムな文字列を記入。 25 I列にQRコードの数式を設定 26 */ 27 for (var i = 1; i < lastrow; i++) { 28 data = values[i][0]; 29 if (data == "") { 30 values[i][0] = getRndStr(); 31 } 32 values[i][8] = false; 33 var qrc1 = '=image(\"https://chart.apis.google.com/chart?chs=250x250&cht=qr&chl=\"&A' + (i + 1) + ')'; 34 values[i][7] = qrc1; 35 } 36 range.setValues(values); //スプレッドシートに書き戻し 37 38 /*回答の名前と一致する行の1列目をidとして取得する*/ 39 for (let row = 1; row <= lastrow; row++) { 40 if (sht.getRange(row, 3).getValue() == name) { 41 var id = sht.getRange(row, 1).getValue(); 42 } 43 } 44 45 //------------------------------------------ 46 // 日程シートから該当日のデータを取得 47 // フォームの送信内容を取得 48 49 // const items = e.response.getItemResponses(); // 削除 50 // const reserveDate = items[2].getResponse(); // 下のように修正 51 const reserveDate = date; // dateをそのまま使う 52 53 // 予約処理 54 const reservationCount = parseInt(child) + 1; // 追加 予約人数(連れの数+1 人) 55 //const result = updateDatesSheet(reserveDate); // 下のように修正。 56 const result = updateDatesSheet(reserveDate, reservationCount ); // 予約人数も渡す。 57 58 // 追加:reserveSheet変数の定義 59 const reserveSheet = ss.getSheetByName("予約"); 60 61 if (result === "success") { 62 reserveSheet.appendRow([mail, reserveDate]); // 予約シートに追記 63 updateForm(); // フォームの「空き状況」を更新 64 } 65 66 // 追加。メール送信時にid, name, childを渡す。 67 sendEmail(mail, date, result, id, name, child); 68} 69 70/** 71 * 引数に指定した予約日の予約人数が、定員オーバーでないか確認する。 72 * オーバーしていなければ、日程シートの現在の予約数に予約人数を加算し、文字列"success"を返す。 73 * オーバーしている場合は、文字列"error"を返す。 74 * 引数:reserveDate フォームに入力された予約日 75 * reservationCount フォームの回答から計算した予約人数 76*/ 77// function updateDatesSheet(reserveDate) { 78function updateDatesSheet(reserveDate, reservationCount ) { // 修正:予約人数を受け取る。 79 const ss1 = SpreadsheetApp.openById("シートID入れてます"); 80 const datesSheet = ss1.getSheetByName("日程"); 81 const table = datesSheet.getDataRange().getValues(); 82 const index = table.findIndex(row => row[0] === reserveDate); 83 84 // 追加:フォームで回答された日付がスプレッドシート中になかった場合の処理。 85 if (index === -1) return "error"; 86 87 const target = { 88 rowNum: index + 1, 89 date: table[index][0], 90 cap: table[index][1], 91 reserved: table[index][3], // 予約済人数:日程シートのD列 92 } 93 94 // 定員超過しなければ「予約済」に加算する 95 //if (target.reserved < target.cap) { // 下記のように修正 96 // [更新前予約人数+フォームで回答された予約人数]と定員とを比較する。 97 if (target.reserved + reservationCount < target.cap) { 98 // datesSheet.getRange(target.rowNum, 3); // 下記のように修正 99 // 更新後予約済人数を日程シートの予約済(C列?)に書き込む。 100 datesSheet.getRange(target.rowNum, 3).setValue(target.reserved + reservationCount); 101 return "success" 102 } else { 103 return "error" 104 } 105} 106 107 108//function sendEmail(mail, preferredDate, result) { 109function sendEmail(mail, preferredDate, result, id, name, child) { // id,name,childを受け取るように修正 110 const mailTitle = "予約結果"; 111 const imageurl = 'https://chart.apis.google.com/chart?chs=250x250&cht=qr&chl=' + id; 112 const response = UrlFetchApp.fetch(imageurl); // option削除 113 const blob = response.getBlob().getAs(MimeType.JPEG); 114 115 const mailBody = (result === "success") 116 ? "予約が完了しました。\n 添付されたQRコードを当日の受付でご提示ください \n" 117 + ` 【予約日】:${preferredDate}` 118 + '【氏 名】' + name + '\n' 119 + '【お申込み人数】' + child + '名\n' 120 + '【来場者ID】' + id 121 : "定員超過のため予約できませんでした。\n" + 122 "下記のフォームから再度申請してください\n" + 123 //form.getPublishedUrl(); //修正。エラーになるのでURLを直接記述する必要あり。 124 "フォームのURL"; 125 126 const option = { 127 method: "get", 128 "attachments": blob, 129 'name': 'イベント', 130 } 131 132 GmailApp.sendEmail(mail, mailTitle, mailBody, option); 133} 134 135function getRndStr() { 136 var str = "abcdefghijklmnopqrstuvwxyz0123456789"; 137 var len = 8; 138 var result = ""; 139 for (var i = 0; i < len; i++) { 140 result += str.charAt(Math.floor(Math.random() * str.length)); 141 } 142 return result; 143} 144 145 146function updateForm() { 147 console.log("ダミーのupdateForm()が呼ばれました。") 148}

投稿2023/09/02 00:47

編集2023/09/02 12:54
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

10-mo

2023/09/02 00:58

SaYGOさん コメントいただきありがとうございます!target部分、色がつくようになりました。 しかし、今度は for (let row = 1; row <= lastrow; row++) { if (sht.getRange(row, 3).getValue() == name) { var id = sht.getRange(row, 1).getValue(); //ここのid部分が反応しなくなってしまった。 } } //------------------------------------------ // 日程シートから該当日のデータを取得 下記のエラーも発生してしまいました。。。 TypeError: Cannot read properties of undefined (reading 'getItemResponses') at form(コード:44:28)
退会済みユーザー

退会済みユーザー

2023/09/02 04:31 編集

updateDatesSheet 関数内の「 // 定員超過しなければ「予約済」に加算する」の 「datesSheet.getRange(target.rowNum, 3)」 の部分は、 入力する位置を指定しているだけで、シートに何も値が入力されていませんが、これは意図する動作なのでしょうか。 意図する動作でない場合、日程シートのC列に、どの変数の値を入力するのでしょうか?(予約人数?) コードも全部ではなく一部しか記載されていないようです。もし記載されていない部分にバグが潜んでいた場合、こちらでは手が出ません。 またソースコードだけ見せられても、こちらでコードから想像しながら手元でスプレッドシートやフォームをゼロから作って動作確認しなければいけないという手間があります。 可能であればフォームとスプレッドシートを全部公開状態にして共有していただけると解析が楽なので助かります。
10-mo

2023/09/02 05:01

質問記載のコードは、スプレッドシートのメニューの拡張機能→「Apps Script」から起動したエディタ経由で書いているという理解でいいですか? →はい、そのようにしています。スプレッドシートとフォームでしか使えないコードがあるんですね、 コードはこれで全てです(-_-;) これで大丈夫かと思っておりました、、、、 こちらにスプレッドシートとフォームを共有させていただきます。よろしくお願いいたします。 https://docs.google.com/spreadsheets/d/1nrCJsjW9FvMsdA8rnCKyl7_2W9rUm2chs1_HoAPslHk/edit?usp=sharing https://docs.google.com/forms/d/e/1FAIpQLScTN900Fnwr6JV-keKM2gFSzwkebjC0DNbBIqc19kK2wlNlEw/viewform?usp=sharing
退会済みユーザー

退会済みユーザー

2023/09/02 05:51

共有ありがとうございます。 > コードはこれで全てです とのことですが、「upDateForm」関数がどこにもありません。この部分は未作成ということでよろしいでしょうか?
10-mo

2023/09/02 06:48

const result = updateDatesSheet(reserveDate); if (result === "success"){ reserveSheet.appendRow([email, reserveDate]); // 予約シートに追記 updateForm(); // フォームの「空き状況」を更新 } ↑これは違いますでしょうか?
退会済みユーザー

退会済みユーザー

2023/09/02 07:49 編集

そこは単に、updateForm関数を"呼び出している部分"ですよね? 先のコメントは「(呼び出される)updateForm関数の"本体"」が、どこにも書いてないように見えます、という意味でした。 呼び出される本体がないと、呼び出し時にエラーになるはずであるため、質問文に記載し忘れていらっしゃったのではないかと思ったのです。 ただ、実際には呼び出す処理に到達される前に別のエラーが発生していて、発覚していなかっただけのようですね。 おそらくupdateForm関数を呼び出さなくても正常に動くような気がしますので、気になさらなくて結構です。 (完全に憶測ですが、updateForm関数の本体は質問者さんが他のサイト等からコピペしてきたときには存在していたが、ガチャガチャとコードをいじる過程で、別の関数の中に組み換えられてしまい、残った呼び出し部分だけ消すのを忘れられているだけではないか、と推察しています。)
10-mo

2023/09/02 09:37

丁寧にご教示いただきありがとうございます! 一つ一つ、勉強して理解を深めたいとおもいます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問