回答編集履歴

6

 

2022/10/26 22:31

投稿

退会済みユーザー
test CHANGED
@@ -1,4 +1,5 @@
1
1
  ※文字数(1つの回答欄に10000字以上は投稿不可)の関係で回答を2つに分けます。
2
+
2
3
  まず修正後の全体のコードを下記に記載します。(修正した部分については後述します) 
3
4
  ```js
4
5
  /*Step1:グローバル定数

5

 

2022/10/26 22:31

投稿

退会済みユーザー
test CHANGED
@@ -1,4 +1,4 @@
1
- 誠に申し訳ありませんが、文字数(1つの回答欄に10000字以上は投稿不可)の関係で回答を2つに分けます。
1
+ ※文字数(1つの回答欄に10000字以上は投稿不可)の関係で回答を2つに分けます。
2
2
  まず修正後の全体のコードを下記に記載します。(修正した部分については後述します) 
3
3
  ```js
4
4
  /*Step1:グローバル定数

4

 

2022/10/26 16:29

投稿

退会済みユーザー
test CHANGED
@@ -1,49 +1,142 @@
1
- (続き
1
+ ※誠に申し訳ありませんが、文字数(1つの回答欄に10000字以上は投稿不可の関係で回答を2つに分けます。
2
- 修正後のコード全体については、別(下)の回答欄に記載ています
2
+ まず修正後の全体のコードを下記記載します。(修正した部分については後述します) 
3
- # 修正したところとポイント
4
-
5
- #### ① チェックボックスの状態を格納する部分
6
- 「/*Step8:宛先リストシート名から、シートの内容を配列で取得する」(130行目あたりから)を見ると
7
3
  ```js
4
+ /*Step1:グローバル定数
5
+ -------------------------------------------------------------*/
6
+ const ss = SpreadsheetApp.getActiveSpreadsheet();
7
+ const mainSheet = ss.getSheetByName("Main");
8
+
9
+ /*Step2:シートオープン時にプルダウンの選択肢をセットする
10
+ -------------------------------------------------------------*/
11
+ function onOpen() {
12
+ setRecipientListPulldown();
13
+ }
14
+
15
+ /*Step3:送信/下書きボタン押下時の処理|onClickCreateDrafts
16
+ -------------------------------------------------------------*/
17
+ function onClickSendEmails() {
18
+ const answer = Browser.msgBox("メールを一括送信します。よろしいですか。", Browser.Buttons.OK_CANCEL);
19
+ if (answer === "ok") {
20
+ batchProcessEmails("sendEmail");
21
+ Browser.msgBox("処理が完了しました。「処理結果」列を確認してください。");
22
+ }
23
+ }
24
+
25
+ function onClickCreateDrafts() {
26
+ const answer = Browser.msgBox("メールの下書きを一括作成します。よろしいですか。", Browser.Buttons.OK_CANCEL);
27
+ if (answer === "ok") {
28
+ batchProcessEmails("createDraft");
29
+ Browser.msgBox("処理が完了しました。「処理結果」列を確認してください。");
30
+ }
31
+ }
32
+
33
+ /*Step4:宛先シート全行分のメールを処理する|batchProcessEmails
34
+ -------------------------------------------------------------*/
35
+ function batchProcessEmails(action) {
36
+ // Mainシートの入力値を取得
37
+ const inputValues = getInputValues();
38
+ // ログ列(R列)をクリア
39
+ ss.getSheetByName(inputValues.recipientListName).getRange("R2:R").clear();
40
+ // 宛先シートの入力内容を取得
41
+ const targets = getTargets(inputValues.recipientListName);
42
+ // 宛先シートの全行に対して送信(または下書き)を実行
43
+ const logs = [];
44
+ for (const target of targets) {
45
+ const result = processEmail(inputValues, target, action);
46
+ logs.push([result]);
47
+ }
48
+ // ログ列(Q列)に結果を貼り付け
49
+ ss.getSheetByName(inputValues.recipientListName)
50
+ .getRange(`R2:R${logs.length + 1}`)
51
+ .setValues(logs);
52
+ }
53
+
54
+ /*Step5:メールを1件送信または下書き作成する|processEmail
55
+ -------------------------------------------------------------*/
56
+ function processEmail(inputValues, target, action) {
57
+ try {
58
+ // タイトル・本文に差込を適用
59
+ const replacedMailTitle = getReplacedString(inputValues.mailTitle, target.replaceList);
60
+ const replacedMailBody = getReplacedString(inputValues.mailBody, target.replaceList);
61
+
62
+ // 添付ファイルを取得
63
+ const attachements = [
64
+ ...getAttachements(inputValues.attachmentFolderId, inputValues.commonAttachmentNames),
65
+ ...getAttachements(inputValues.attachmentFolderId, target.eachAttachmentNames)
66
+ ]
67
+
68
+ /* メールオプションの設定
69
+ -------------------------------------------------------------*/
70
+ const options = {};
71
+ if (inputValues.senderEmail) options.from = inputValues.senderEmail;
72
+ if (inputValues.senderName) options.name = inputValues.senderName;
73
+ if (target.cc) options.cc = target.cc;
74
+ if (target.bcc) options.bcc = target.bcc;
75
+ if (attachements.length) options.attachments = attachements;
76
+
77
+ /* メール送信 or 下書き作成
78
+ -------------------------------------------------------------*/
79
+ if (action === "sendEmail") {
80
+ GmailApp.sendEmail(target.to, replacedMailTitle, replacedMailBody, options);
81
+ } else {
82
+ GmailApp.createDraft(target.to, replacedMailTitle, replacedMailBody, options);
83
+ }
84
+ return "Success";
85
+ } catch(error) {
86
+ return error;
87
+ }
88
+ }
89
+
90
+ /*Step6:文字列, 差込リストから差込後のテキストを取得する|getReplacedString
91
+ -------------------------------------------------------------*/
92
+ function getReplacedString(string, replaceList) {
93
+ let replacedString = string;
94
+ for (const item of replaceList) {
95
+ const before = new RegExp(`\{\{ *${item.before} *\}\}`, "g");
96
+ replacedString = replacedString.replace(before, item.after);
97
+ }
98
+ return replacedString;
99
+ }
100
+
101
+ //★:チェックボックスの判定
102
+
103
+ function getCheckbox() {
104
+ const myCell = mainSheet.getActiveCell();
105
+ const rule = myCell.getDataValidation();
106
+ if (rule != null) {
107
+ const criteria = rule.getCriteriaType();
108
+ const status = myCell.getValue()
109
+ if ( criteria == 'CHECKBOX' && status == true) {
110
+ const row = myCell.getRow();
111
+ getInputValues();
112
+ }
113
+ }
114
+ }
115
+
116
+ /*Step7:mainシートに入力された値を取得する|getInputValues
117
+ -------------------------------------------------------------*/
118
+ function getInputValues() {
119
+ const inputValues = {
120
+ attachmentFolderId: mainSheet.getRange("B2").getValue(), // 添付ファイル格納フォルダID
121
+ mailTitle: mainSheet.getRange("B5").getValue(), // メールタイトル
122
+ mailBody: mainSheet.getRange("B6").getValue(), // 本文
123
+ commonAttachmentNames: [
124
+ mainSheet.getRange("B7").getValue(), // 共通添付ファイル名1
125
+ mainSheet.getRange("B8").getValue(), // 共通添付ファイル名2
126
+ mainSheet.getRange("B9").getValue(), // 共通添付ファイル名3
127
+ ],
128
+ recipientListName: mainSheet.getRange("B12").getValue(), // 宛先リストシート名
129
+ }
130
+ return inputValues;
131
+ }
132
+
133
+ /*Step8:宛先リストシート名から、シートの内容を配列で取得する
134
+ -------------------------------------------------------------*/
8
135
  function getTargets(recipientListName) {
9
-
136
+ const targetsSheet = ss.getSheetByName(recipientListName);
10
- ...
137
+ const sheetData = targetsSheet.getDataRange().getValues();
11
- ......
138
+ const header = sheetData[0];
12
- const targets = contents.map((row) => {
139
+ const contents = sheetData.slice(1);
13
- return {
14
- checkbox: [0], // チェックボックス
15
- ```
16
- となっていました。
17
- この一番下の行の
18
- 「checkbox: [0]」
19
- というところを
20
- 「checkbox: row[0]」
21
- に修正しています。
22
-
23
- おそらく単純ミスではないかな、と思いました。
24
- その下の行では
25
- ```js
26
- to: row[1], // 宛先メールアドレス
27
- cc: row[2], // ccアドレス
28
- bcc: row[3],
29
- ```
30
- のように、「row[x]」という形になっていますね。
31
-
32
- row[0]ではなく[0]としてしまうと、単に数字のゼロが格納されるだけなので、チェックボックスの状態を格納したことになりません。
33
-
34
-
35
-
36
- #### ② チェックボックスを入れた宛先だけを送付させるには?
37
- ご質問の中にある「チェックのついたデータのみを取得できません。」という文から察するに、
38
- 質問者さんのコードだと「チェックを入れた行だけメール送信したいのに、全部の行に記入したメールが送信されてしまう。」ということであると推測しました。
39
-
40
- そして上に書いた ① の部分を直して試しに「送信」ボタンまたは「下書き」ボタンを押しても、やはり全部送信されてしまいます。
41
-
42
- これはなぜかというと「チェックを入れた行だけを送信対象とする」という、条件によるふるい分け処理が欠けているからです。
43
-
44
- ではどのようにするかについて説明します。
45
- まず、送信対象を作っている部分は(①で言及したのと同じ) Step8 : getTargets() 関数の中の、以下の部分になります。
46
- ```js
47
140
  const targets = contents.map((row) => {
48
141
  return {
49
142
  checkbox: row[0], // [修正] // チェックボックス
@@ -57,56 +150,67 @@
57
150
  row[16], // 個別添付ファイル名3
58
151
  ]
59
152
  };
153
+ }).filter(e => e.checkbox === true); // [追加]
154
+ console.log(targets);
155
+ return targets;
156
+ }
157
+
158
+
159
+ /*Step8:ヘッダー行・値行から、置換前・置換後を格納したオブジェクトの配列を返す
160
+ -------------------------------------------------------------*/
161
+ function getReplaceList(header, row) {
162
+ const beforeStrings = header.slice(4, 14); // 5 - 14列目が差込項目
163
+ const afterStrings = row.slice(4, 14); // 5 - 14列目が差込項目
164
+ const replaceList = [];
165
+ beforeStrings.forEach((beforeString, index) => {
166
+ if (beforeString) replaceList.push({
167
+ before: beforeString,
168
+ after: afterStrings[index]
169
+ });
60
170
  });
171
+ return replaceList;
172
+ }
173
+
174
+ /* -------------------------------------------
175
+ getAttachments
176
+ フォルダID, ファイル名配列からファイル配列を取得する
177
+ -------------------------------------------- */
178
+ function getAttachements(folderId, fileNames) {
179
+ const attachements = [];
180
+ fileNames.forEach((fileName) => {
181
+ if (fileName) attachements.push(getAttachement(folderId, fileName));
182
+ })
183
+ return attachements;
184
+ }
185
+
186
+ /* ----------------------------------------
187
+ getAttachment
188
+ フォルダID, ファイル名からファイルを取得する
189
+ ------------------------------------------- */
190
+ function getAttachement(folderId, fileName) {
191
+ const files = DriveApp.getFolderById(folderId).getFilesByName(fileName);
192
+ if (!files.hasNext()) throw Error(`添付ファイル ${fileName} が存在しません。`);
193
+ file = files.next();
194
+ if (files.hasNext()) throw Error(`添付ファイル ${fileName} が複数存在します。`);
195
+ return file;
196
+ }
197
+
198
+ /* -----------------------------------------
199
+ setRecipientListPulldown
200
+ 宛先リストシート名のプルダウンに選択肢をセットする
201
+ ------------------------------------------ */
202
+ function setRecipientListPulldown() {
203
+ // Mainシート以外のシート名を取得
204
+ const values = ss.getSheets().map((sheet) => {
205
+ const sheetName = sheet.getName()
206
+ return sheetName !== "Main"
207
+ ? sheetName
208
+ : ""
209
+ })
210
+ // 入力規則を作成
211
+ const rule = SpreadsheetApp.newDataValidation().requireValueInList(values).build();
212
+ // MainシートのB14セルにルールを適用
213
+ ss.getSheetByName("Main").getRange("B14").setDataValidation(rule);
214
+ }
61
215
  ```
62
- contents.map の「map」 は配列を作る関数です。
63
- 「contents」 には、宛先シート全行データが格納されています
216
+ (修正部分の説明ついて回答欄に記載しています
64
- そこから1行1行取り出して、必要な列だけ格納している処理が、上記のコードの部分です。
65
-
66
- では、チェックを付けた行だけに絞るにはどうすればよいでしょうか?
67
-
68
- ここで「filter」という関数を使用します。
69
- filter 関数は、配列のうち「条件にあてはまる要素だけ」を抜き出して、新たな配列として返す関数です。
70
- 簡単な例で説明します。
71
- たとえば
72
- numbers = [ 1, 2, 3, 4, 5, 6]
73
- という配列があるとして、この配列から、偶数だけを抜き出したいとします。
74
- これをコードで表すと
75
- ```js
76
- const numbers = [1, 2, 3, 4, 5, 6];
77
- const new_numbers = numbers.filter(e => e % 2 === 0);
78
- console.log(new_numbers);
79
-
80
- >>> [2, 4, 6]
81
- と表示される
82
- ```
83
-
84
- となります。
85
- filterの中の「e => e % 2 === 0」というのが条件で「numbersの要素の中で、2で割った余りが0になる数だけを抜き出す」という条件を指定していることになります。
86
-
87
- これを応用して、上記の宛先リストのコードに適用すると
88
- 「チェックボックスにチェックが入っている(=チェックボックスの値が true)であるものだけを抜き出す」という条件になります。
89
-
90
- これをコードで書くと
91
- ```js
92
- const targets = contents.map((row) => {
93
- return {
94
- checkbox: row[0], // [修正] // チェックボックス
95
- to: row[1], // 宛先メールアドレス
96
- cc: row[2], // ccアドレス
97
- bcc: row[3], // bccアドレス
98
- replaceList: getReplaceList(header, row), // 置換内容
99
- eachAttachmentNames: [
100
- row[14], // 個別添付ファイル名1
101
- row[15], // 個別添付ファイル名2
102
- row[16], // 個別添付ファイル名3
103
- ]
104
- };
105
- }).filter(row => row.checkbox === true); // [追加]
106
- ```
107
- というようになります。(filter関数の条件を最後に追加)
108
-
109
- いったん、contents =全行について列を整えた配列を作り、さらにその配列の中から「チェックボックスにチェックが入っている行」だけを抜き出した新たな配列を作る、という処理になります。
110
-
111
-
112
-

3

 

2022/10/26 14:49

投稿

退会済みユーザー
test CHANGED
@@ -14,8 +14,14 @@
14
14
  checkbox: [0], // チェックボックス
15
15
  ```
16
16
  となっていました。
17
+ この一番下の行の
18
+ 「checkbox: [0]」
19
+ というところを
17
- この一番下の行の「checkbox: [0]」というところを「checkbox: row[0]」に修正しています。
20
+ 「checkbox: row[0]」
21
+ に修正しています。
22
+
18
- おそらく単純ミスではないかな、と思いました。その下の行では
23
+ おそらく単純ミスではないかな、と思いました。
24
+ その下の行では
19
25
  ```js
20
26
  to: row[1], // 宛先メールアドレス
21
27
  cc: row[2], // ccアドレス

2

 

2022/10/26 14:49

投稿

退会済みユーザー
test CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  #### ① チェックボックスの状態を格納する部分
6
6
  「/*Step8:宛先リストシート名から、シートの内容を配列で取得する」(130行目あたりから)を見ると
7
- ```
7
+ ```js
8
8
  function getTargets(recipientListName) {
9
9
 
10
10
  ...
@@ -37,7 +37,7 @@
37
37
 
38
38
  ではどのようにするかについて説明します。
39
39
  まず、送信対象を作っている部分は(①で言及したのと同じ) Step8 : getTargets() 関数の中の、以下の部分になります。
40
- ```
40
+ ```js
41
41
  const targets = contents.map((row) => {
42
42
  return {
43
43
  checkbox: row[0], // [修正] // チェックボックス
@@ -66,7 +66,7 @@
66
66
  numbers = [ 1, 2, 3, 4, 5, 6]
67
67
  という配列があるとして、この配列から、偶数だけを抜き出したいとします。
68
68
  これをコードで表すと
69
- ```
69
+ ```js
70
70
  const numbers = [1, 2, 3, 4, 5, 6];
71
71
  const new_numbers = numbers.filter(e => e % 2 === 0);
72
72
  console.log(new_numbers);

1

 

2022/10/26 14:48

投稿

退会済みユーザー
test CHANGED
@@ -1,4 +1,5 @@
1
1
  (続き)
2
+ 修正後のコード全体については、別(下)の回答欄に記載しています。
2
3
  # 修正したところとポイント
3
4
 
4
5
  #### ① チェックボックスの状態を格納する部分