回答編集履歴
6
test
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
※文字数(1つの回答欄に10000字以上は投稿不可)の関係で回答を2つに分けます。
|
2
|
+
|
2
3
|
まず修正後の全体のコードを下記に記載します。(修正した部分については後述します)
|
3
4
|
```js
|
4
5
|
/*Step1:グローバル定数
|
5
test
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
※
|
1
|
+
※文字数(1つの回答欄に10000字以上は投稿不可)の関係で回答を2つに分けます。
|
2
2
|
まず修正後の全体のコードを下記に記載します。(修正した部分については後述します)
|
3
3
|
```js
|
4
4
|
/*Step1:グローバル定数
|
4
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 t
|
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
|
-
|
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
test
CHANGED
@@ -14,8 +14,14 @@
|
|
14
14
|
checkbox: [0], // チェックボックス
|
15
15
|
```
|
16
16
|
となっていました。
|
17
|
+
この一番下の行の
|
18
|
+
「checkbox: [0]」
|
19
|
+
というところを
|
17
|
-
|
20
|
+
「checkbox: row[0]」
|
21
|
+
に修正しています。
|
22
|
+
|
18
|
-
おそらく単純ミスではないかな、と思いました。
|
23
|
+
おそらく単純ミスではないかな、と思いました。
|
24
|
+
その下の行では
|
19
25
|
```js
|
20
26
|
to: row[1], // 宛先メールアドレス
|
21
27
|
cc: row[2], // ccアドレス
|
2
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
test
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
(続き)
|
2
|
+
修正後のコード全体については、別(下)の回答欄に記載しています。
|
2
3
|
# 修正したところとポイント
|
3
4
|
|
4
5
|
#### ① チェックボックスの状態を格納する部分
|