こんにちは、ばばーるです。
前回のおさらい
前回はキリン部長からの納期管理のリマインダーメール
- スプレッドシート上の日付を読み込める。
- 日付の出力形式は曜日 月 日 年 HH:MM:SS 標準時]で出力される。
今回は依頼内容の全体像をざっと説明します。
全体像をキリン部長と打ち合わせ

キリン部長。
もうちょっと具体的に依頼内容を教えてください。

こんな感じでお願いしたんだけど…。
1. 納期が迫る、3,2,1カ月、2週間、前日にリマインドメールを送る。
2. 納期遅れの書類は毎月10日にリマインドメールを送る。
3. メールは営業日に送信する。

提出期限の2週間前の日が営業日じゃない場合はどうします?

前倒しでお願いします。

かしこまりました!
最初に答えを載せちゃいましょう

ギブ。
助けてGASエモン

オイッ!
ということで、たぬきさんがとりあえずギブしましたが、全体像を把握してから理解を進めていく方が理解しやすいと思うので、最初にスクリプト全体を載せてから解説していきます。
スクリプトのシートは2枚書いていきます。
・reminderEmail.gsはメインのスクリプトです。
・subFunctionsはサブのスクリプトで、処理で使う関数がまとめられています。

reminderEmailのスクリプトを書いていきます。
function reminderEmail() {
//Get Spreadsheet data
const id = '提出書類管理シートのスプレッドシートID';
const ss = SpreadsheetApp.openById(id);
const sheet = ss.getSheetByName('納期');
const submitData = sheet.getSheetValues(1, 1, sheet.getLastRow(), 5);
//Send remind emails on business days
const submitDate = submitData.map(a => a[4]);
const varianceDay = [90, 60, 30, 14, 1];
let today = new Date(new Date().setHours(0, 0, 0, 0));
submitDate.forEach(function(day, i){
if (i > 0){
let date4email = convertDate(submitData[i][4]);
if (day - today < 0) {
let futureDay = new Date(today.getFullYear() + '/' + Number(today.getMonth() + 1) + '/10');
futureDay = decideSendday(futureDay);
if (futureDay - today == 0){
delayEmail(submitData[i][3], submitData[i][2], submitData[i][0], submitData[i][1], date4email);
}
} else if (day - today > 0) {
varianceDay.forEach(b => {let date = new Date(day);
let remindDay = new Date(date.setDate(date.getDate() - b));
remindDay = decideSendday(remindDay);
if(remindDay - today == 0){
remindEmail(submitData[i][3], submitData[i][2], submitData[i][0], submitData[i][1], date4email);
}
});
}
}});
}
subFunctionsのスクリプトはこちらです。
/*
Subfunctions
1. Judge if the day is business day or not
2. Change the day as 1 day before
3. Convert date from GAS dat to yyyy/mm/dd(week)
4. Send emails in case of delivery delay
5. Send emails in case of 90, 60, 30, 14, 1 day before
*/
//--------------------------------------------//
// 1. Judge if the day is business day or not //
//--------------------------------------------//
function isBusinessday(date) {
//Identify Japanese holiday
const jpCalendarID = 'en.japanese#holiday@group.v.calendar.google.com';
const jpCalendar = CalendarApp.getCalendarById(jpCalendarID);
const jpCalendarEvent = jpCalendar.getEventsForDay(date).length
//Identify Saturday (= 6) & Sunday (= 0)
const week = date.getDay();
//Identify company holiday
const month = date.getMonth() + 1;
const day = date.getDate();
const companyHoliday = [[5, 1], [12, 29], [12, 30], [12, 31], [1, 2], [1, 3], [1, 4]];
//Check hoidays
let i = 0
companyHoliday.forEach(function(ch){
if (month == ch[0] && day == ch[1] || week == 6 || week == 0 || jpCalendarEvent == 1){
i++;
}
});
if (i > 0){
return false;
} else {
return true;
}
}
//--------------------------------------------//
// 2. Change the day as 1 day before //
//--------------------------------------------//
function decideSendday(date) {
while(isBusinessday(date) !== true){
date = new Date(date.setDate(date.getDate() - 1));
}
return date;
}
//------------------------------------------------//
// 3. Convert date from GAS dat to yy/mm/dd(week) //
//------------------------------------------------//
function convertDate(submitDay){
const Year = submitDay.getFullYear();
const Month = submitDay.getMonth() + 1;
const date = submitDay.getDate();
const day = submitDay.getDay();
const week = ['日', '月', '火', '水', '木', '金', '土'];
const dateInemail = Year + '/' + Month + '/' + date + '(' + week[day] + ')';
return dateInemail;
}
//--------------------------------------------//
// 4. Send an email in case of delivery delay //
//--------------------------------------------//
function delayEmail(to, name, no, docname, day){
let subject = '提出書類について';
let body = '';
let html = name + '様<br><br>';
html += '<strong>' + no + ': ' + docname + '</strong>'
html += 'の提出期限は' + '<u>' + day + '</u>' + 'でした。<br>'
html += '<a href="提出書類管理シートのスプレッドシートURL">提出書類管理シート</a>をご確認の上、';
html += '書類の提出をお願いいたします。<br><br>';
html += 'ご不明な点がございましたらご連絡いただければ幸いです。<br>';
html += 'Zoo株式会社<br>';
html += '書類管理グループ グループ長<br>';
html += 'キリン部長<br>';
const id = 'logoが保存されているドライブのID';
const image = DriveApp.getFilesByName('logo.jpeg').next();
html += "<img src='cid:logo'>"
GmailApp.sendEmail(to, subject, body,
{htmlBody : html,
inlineImages : {logo: image.getBlob()},
name: 'キリン部長'});
}
//----------------------------------------------------------//
// 5. Send an email in case of 90, 60, 30, 14, 1 day before //
//----------------------------------------------------------//
function remindEmail(to, name, no, docname, day){
let subject = '提出書類について';
let body = '';
let html = name + '様<br><br>';
html += '<strong>' + no + ': ' + docname + '</strong>'
html += 'の提出期限が' + '<u>' + day + '</u>' + 'となっております。<br>'
html += '<a href="提出書類管理シートのスプレッドシートURL">提出書類管理シート</a>も送付いたします。<br>';
html += '納期が送れそうな場合は必ずご一報ください。<br><br>';
html += 'ご不明な点がございましたらご連絡いただければ幸いです。<br>';
html += 'Zoo株式会社<br>';
html += '書類管理グループ グループ長<br>';
html += 'キリン部長<br>';
const id = 'logoが保存されているドライブのID';
const image = DriveApp.getFilesByName('logo.jpeg').next();
html += "<img src='cid:logo'>"
GmailApp.sendEmail(to, subject, body,
{htmlBody : html,
inlineImages : {logo: image.getBlob()},
name: 'キリン部長'});
}
これだけ見ると難解に見えますが、一つ一つ説明してくので安心してください。
reminderEmailの概説
reminderEmail の上から読んでいきましょう。スプレッドシートの内容を参照しながら読み解くと分かり易いと思います。

function reminderEmail() {
//Get Spreadsheet data
(1行目)const id = '提出書類管理シートのスプレッドシートID';
(2行目)const ss = SpreadsheetApp.openById(id);
(3行目)const sheet = ss.getSheetByName('納期');
(4行目)const submitData = sheet.getSheetValues(1, 1, sheet.getLastRow(), 5);
1行目:スプレッドシートIDを変数idに代入
2行目:SpreadsheetApp.OpenByIdでスプレッドシートにアクセス
3行目:getSheetByNameでスプレッドシートの’納期’というシートにアクセス
4行目:getSheetValuesでセルA1からE5までのセルの値を取得
ここら辺の内容がよくわからない場合は以下の記事くらいから過去記事を辿ってみてください。
//Send remind emails on business days
(5行目)const submitDate = submitData.map(a => a[4]);
(6行目)const varianceDay = [90, 60, 30, 14, 1];
(7行目)let today = new Date(new Date().setHours(0, 0, 0, 0));
5行目:4行目で取得したA1からE5までのセルの値の内、E列だけをmap関数で取得
6行目:3、2、1カ月、2週間、1日前の日付を取得するための配列を設定
7行目:今日の日付をnew Date()を取得して、時間を00:00:00に設定

ちょっと待った。
もうこの時点でお腹いっぱいだ。

すごく分かる。
でもとりあえず全体の構成を理解するために一気に読んでいこう。
(8行目)submitDate.forEach(function(day, i){
(9行目)if (i > 0){
(10行目)let date4email = convertDate(submitData[i][4]);
(11行目)if (day - today < 0) {
(12行目)let futureDay = new Date(today.getFullYear() + '/' + Number(today.getMonth() + 1) + '/10');
(13行目)futureDay = decideSendday(futureDay);
(14行目)if (futureDay - today == 0){
(15行目) delayEmail(submitData[i][3], submitData[i][2], submitData[i][0], submitData[i][1], date4email);
}
(16行目)} else if (day - today > 0) {
(17行目)varianceDay.forEach(b => {let date = new Date(day);
(18行目)let remindDay = new Date(date.setDate(date.getDate() - b));
(19行目)remindDay = decideSendday(remindDay);
(20行目)if(remindDay - today == 0){
(21行目)remindEmail(submitData[i][3], submitData[i][2], submitData[i][0], submitData[i][1], date4email);
}
});
}
(22行目)}});
8~22行目:forEach文で提出期限のデータを1行づつ読み込んで
9~22行目:if (i > 0)で1行目(i = 0)の”提出期限”以降の2行目から読み込む
10行目:convertDateというsubfunctionの関数を呼び出す
11~15行目:納期遅れの提出書類のリマインドメールを送信
16~21行目:3,2,1カ月、2週間、前日に提出書類のリマインドメールを送信

一番重要な11~21行目の説明雑だな。

ここらはsubfunctionが絡んでいるから、次回以降詳しく説明していくよ。
subFunctionsの概説
/*
Subfunctions
1. Judge if the day is business day or not
2. Change the day as 1 day before
3. Convert date from GAS dat to yyyy/mm/dd(week)
4. Send emails in case of delivery delay
5. Send emails in case of 90, 60, 30, 14, 1 day before
*/
subFunctionsのには5つの関数が収納されています。
1.はisBusinessdayという関数で、土日祝に加えて、会社の休日除いた営業日かどうかの判断を行えます。
2.は営業日でない場合に、日付を1日づつずらす関数です。
3.はリマインドメールを送る際に、日付を2020/12/28(月)の様なyyyy/mm/dd(week)という形式に変換する関数です。
4.と5.はGmailを決められた文章形式にして送信するための関数です。詳細は以下のリンクをご参照ください。

もう限界です。
詳しく教えてください。

りょ。
まとめ
今回はキリン部長からの依頼内容の全体像をざっと説明しました。キリン部長からの依頼は
- 納期が迫る、3,2,1カ月、2週間、前日にリマインドメールを送る。
- 納期遅れの書類は毎月10日にリマインドメールを送る。
- メールは営業日に送信する。
それに対して、スクリプトは
- スプレッドシートから提出期限を取得
- 3,2,1カ月、2週間、前日の日付を計算
- 営業日を認識
- メールを送信
をしているらしいことがわかります。
今回だけで理解するのは難しいので次回から詳細を説明していきます。
コメント
[…] […]