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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Google スプレッドシート

Google スプレッドシートは、フリーで利用できる表計算ソフト。Webアプリのためインターネットに接続することで利用できます。チャートやグラフの作成のほか、シートを他のユーザーと共有したり、同時に作業を進めることも可能です。

Q&A

解決済

1回答

592閲覧

自動通知メールにて 該当する内容をまとめてメール

ishida8258

総合スコア2

Google スプレッドシート

Google スプレッドシートは、フリーで利用できる表計算ソフト。Webアプリのためインターネットに接続することで利用できます。チャートやグラフの作成のほか、シートを他のユーザーと共有したり、同時に作業を進めることも可能です。

0グッド

0クリップ

投稿2023/02/13 05:57

編集2023/02/14 02:21

実現したいこと

  • 使用期限がきたものに対し全てまとめてメールで、

  通知メールは1件1件通知されるものではなくまとめて送られてくる

前提

https://blog.sugiokasystem.co.jp/entry/2022/06/03/171226
https://design-remarks.com/gas-spred-mailalert/
上記サイト二件を見ながら試してみたのですが、まったくわからずでした。
for分をメール内容のところに

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

イメージ説明
上記画像の内容を抽出してメールしてもらっていて
使用期限 登録している日付にきたら
公開 済の場合はメールをしない
未の場合はメールをする
イメージ説明
このようにメールがくるんですが、複数件ある場合はメールが1件に対し1件きてしまいます。

イメージ説明
コードはこのようになっています。

該当のソースコード

function mailAlerts() { //シートを取得 var bk = SpreadsheetApp.getActiveSpreadsheet(); var sh = bk.getActiveSheet(); var last_row = sh.getLastRow(); //最後の行を取得(繰り返し処理の回数) var begin_row = 2; // 処理を開始する行 (1行目は項目名なので2から) //今日の日付を取得し文字列を成型する var today = new Date();   var formatDate = Utilities.formatDate(today, "JST","yyyy/MM/dd"); //繰り返し処理(1行づつ処理し、行の数だけ繰り返す) for(var i = begin_row; i <= last_row; i++) {   //それぞれのセルの中身を取得していく //使用期限(日付は成型する) var sell1 = "E"+i; var value1 = new Date(sh.getRange(sell1).getValue()); //修正後 var value1 = Utilities.formatDate(value1, "JST","yyyy/MM/dd"); //品番 var sell2 = "B"+i; var value2 = sh.getRange(sell2).getValue(); //モデル var sell3 = "C"+i; var value3 = sh.getRange(sell3).getValue(); //URL var sell5 = "D"+i; var value5 = sh.getRange(sell5).getValue(); //使用期限 var sell6 = "E"+i; var value6 = sh.getRange(sell6).getValue(); //公開 var sell7 = "F"+i; var value7 = sh.getRange(sell7).getValue();   //もし、今日の日付と納期が同じで、なおかつ状況が完了でない場合メールを送る if(formatDate == value1 && value7 !== "未"){ GmailApp.sendEmail('tesuto@tesuo', '本日モデルの使用期限'+ value6, '■品番:' + value2 + '■モデル:'+ value3 + '■URL:'+ value5 + '\n■使用期限:'+ value6); } } }

試したこと

if(formatDate == value1 && value7 !== "未"){
GmailApp.sendEmail('tesuto@tesuo',
'本日モデルの使用期限'+ value6,
'■品番:' + value2 + '■モデル:'+ value3 + '■URL:'+ value5 + '\n■使用期限:'+ value6);

こちらを増やして見たが意味なし。
forを使って最後まで該当する項目を探してもらいやってみましたが。
どのように表示していいかわからずで止まってしまいました。

画像を追加しました
10:57
イメージ説明

さらに追加しました
イメージ説明
追加しました
イメージ説明

function alertTasks() { const ss = SpreadsheetApp.getActiveSpreadsheet(); const sheet = ss.getSheetByName('モデル管理'); // 本日の日付を取得 const now = new Date(); // 年月日はそのままで、時間を0時0分0秒にする const date = new Date(now.getFullYear(), now.getMonth(), now.getDate()); // 日付を、'2023/2/14'の形式の文字列にする const formatedDate = Utilities.formatDate(date, 'JST', 'yy/MM/dd'); // 全範囲の値を取得(1行目は項目名なので除く) const values = sheet.getDataRange().getValues().slice(1); // 条件に合致するものに絞り、新規配列を生成 const tasks = values.filter(record => { // 本日の年月日と使用期限の年月日が同じかどうか判定 const isExpiredToday = date.getTime() === record[4].getTime(); // 公開が「未」かどうか判定 const isNotPublished = record[5] === '未'; // どちらもtrueの場合、その行を新しい配列に入れる return isExpiredToday && isNotPublished; }); // 配列の長さが0なら、この関数をここで実行終了(メールを送らない) if (tasks.length === 0) return; // 送信先のメールアドレス const recipient = '送信先のメールアドレス'; // タイトル const subject = `本日使用期限のモデル: ${formatedDate}`; // 本文を生成 const body = tasks.map(([_, pn, model, url]) => { let tempBody = `■品番: ${pn}\n`; tempBody += `■モデル: ${model}\n`; tempBody += `■URL: ${url}\n`; tempBody += `■使用期限: ${formatedDate}\n`; return tempBody; }).join('\n'); // メールを送信 GmailApp.sendEmail(recipient, subject, body); }

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

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

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

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

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

Cocode

2023/02/13 23:35 編集

varは古い変数の書き方で現代ではほぼ使われておりません。 varには欠点があるため、letやconstといった新しい宣言方法が追加されました。 varで書かれているサイトは参考になさらない方がいいです。 また、日付の新しいブログ記事なのにvarが使われている場合、モダンな(=現在主流の)コードの書き方を知らない筆者だと思われます。 そんな方が良いコードを書けるとは思えません。 よって、いずれにせよ参考になさらない方がいいと思います。
guest

回答1

0

ベストアンサー

不具合の原因

for文の中で、1行ずつ条件に合致するたびにメールを送信する処理をしているからです。
for文の中で本来すべきことは、条件に合致するかどうか判定し、合致する場合本文の文字列に加えるという処理です。
そしてメール送信はforの外に書きます。

コード例

forを使わない方が簡潔にできますので、以下の方法をご紹介します。

イメージ説明

javascript

1function alertTasks() { 2 const ss = SpreadsheetApp.getActiveSpreadsheet(); 3 const sheet = ss.getSheetByName('Sheet1'); 4 5 // 本日の日付を取得 6 const now = new Date(); 7 // 年月日はそのままで、時間を0時0分0秒にする 8 const date = new Date(now.getFullYear(), now.getMonth(), now.getDate()); 9 // 日付を、'2023/2/14'の形式の文字列にする 10 const formatedDate = Utilities.formatDate(date, 'JST', 'yy/MM/dd'); 11 12 // 全範囲の値を取得(1行目は項目名なので除く) 13 const values = sheet.getDataRange().getValues().slice(1); 14 15 // 条件に合致するものに絞り、新規配列を生成 16 const tasks = values.filter(record => { 17 // 本日の年月日と使用期限の年月日が同じかどうか判定 18 const isExpiredToday = date.getTime() === record[4].getTime(); 19 // 公開が「未」かどうか判定 20 const isNotPublished = record[5] === '未'; 21 22 // どちらもtrueの場合、その行を新しい配列に入れる 23 return isExpiredToday && isNotPublished; 24 }); 25 26 // 配列の長さが0なら、この関数をここで実行終了(メールを送らない) 27 if (tasks.length === 0) return; 28 29 // 送信先のメールアドレス 30 const recipient = '送信先のメールアドレス'; 31 // タイトル 32 const subject = `本日使用期限のモデル: ${formatedDate}`; 33 // 本文を生成 34 const body = tasks.map(([_, pn, model, url]) => { 35 let tempBody = `■品番: ${pn}\n`; 36 tempBody += `■モデル: ${model}\n`; 37 tempBody += `■URL: ${url}\n`; 38 tempBody += `■使用期限: ${formatedDate}\n`; 39 40 return tempBody; 41 }).join('\n'); 42 43 // メールを送信 44 GmailApp.sendEmail(recipient, subject, body); 45}

投稿2023/02/13 22:37

編集2023/02/13 23:00
Cocode

総合スコア2314

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

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

ishida8258

2023/02/14 01:23

ご丁寧なご対応ありがとうございます とても分かりやすく大変助かりました。 導入したところエラーがでてしまい調べてみても改善することができずでした。 大変お忙しいところ恐縮ですが、こちらのエラー内容を直す方法をご存じでしょうか。 シートの名前を変えてみたりみたのですが、ダメでした。 エラー TypeError: Cannot read properties of null (reading 'getDataRange') alertTasks @ 無題.gs:13
Cocode

2023/02/14 01:26

シート名あってますか? 3行目、ご自身のシート名に変更してくださいね。 const sheet = ss.getSheetByName('Sheet1');
ishida8258

2023/02/14 01:49

ありがとうございます! スプレッドシートのシート名を初期から変更しなおして、 書き換えをしたら成功致しました。 ですが新たなエラーでrecordとはスプレッドシートの列なんでしょうか お調べをしたのですが、こちらの高度なコードがどのようなものかわからずでして大変申し訳ございません。 TypeError: record[4].getTime is not a function (匿名) @ 無題.gs:18 alertTasks @ 無題.gs:16
Cocode

2023/02/14 01:53

私の回答にあるスプレッドシートの画像と、質問者様のスプレッドシートは全く同じ構造ですか? つまり、E列に日付ははいっていますか?
ishida8258

2023/02/14 01:58

ご回答ありがとうございます。 スプレッドシートは同様にE列に日付を入れております。 大変お手数おかけし申し訳ございません。
Cocode

2023/02/14 02:01 編集

質問者様のスクリーンショットを拝見すると、1行目が空白、2行目が項目名、3行目からデータが始まっているように見えます。 私の画像と構造違いませんか? (私は、1行目が項目名、2行目からデータが始まっています〜)
Cocode

2023/02/14 02:03

もし3行目からデータが始まっているのであれば、 コードの13行目を変更してください。 .slice(1); → .slice(2); もし4行目からデータが始まっているのであれば、.slice(3); にしてください。
ishida8258

2023/02/14 02:10

ありがとうございます! Cocode様と同じ内容にスプレッド変更したのですが エラー TypeError: record[4].getTime is not a function (匿名) @ 無題.gs:18 alertTasks @ 無題.gs:16 同上のエラーが出てしまいました。 お忙しいところ大変申し訳ございません
Cocode

2023/02/14 02:14 編集

スプレッドシートのE列の日付の部分をダブルクリックしたら、カレンダーが出てきて他の日付を選択できる状態ですか?
ishida8258

2023/02/14 02:16

E欄の日付はスクショお送りさせていただきました。 選択ができる状態です。
Cocode

2023/02/14 02:18 編集

ではちゃんと「日付型」という種類の値になっていますね。 うーん、、、他に原因が思いつきません。 私のコードをコピペして、一部変更してお使いかと思われますが、その、今お使いのコードを全てご提示いただくことは可能でしょうか?
ishida8258

2023/02/14 02:22

ありがとうございます。 色々と本当にありがとうございます。 変更点は現在はシート名のみで他は後ほど調べながら対応しようかと思っておりました。
Cocode

2023/02/14 02:25 編集

ご提示ありがとうございます! コードは問題なさそうですね…ますます原因が不明です。 確認していただきたいことがあります。 13行目と15行目の間に、以下のコードを2行追加して関数を実行し、コンソールに表示されるものをコピペして見せていただけませんか? console.log(values); console.log(typeof values[1][4]);
ishida8258

2023/02/14 02:47

ありがとうございます! コードに関数を追加してみたところ 100行目ぐらいに隠れた文字がいてそれが邪魔をしていたようです。 それを消したところ全てうまくいきました。 本当にありがとうございました。 とても丁寧でこんなにもご教授頂き感謝しきれません。
Cocode

2023/02/14 02:56

解決してよかったです〜!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問