Time-driven trigger (daily) not triggering every day - google-apps-script

I have the similar issue as described at How to make sure a daily time trigger runs?.
I have a specific script in one of the Google sheets with a daily trigger (time-driven, should trigger every morning, set up through interface, not programmatically). But the script doesn't execute every day. I can see this in the execution report, where there're just successful executions and no failed ones. I can also see if the script executed by checking a cell in the sheet which gets updated with the execution timestamp when the script runs. And I've set up an immediate notification for the failed executions in the trigger settings.
In my specific case, the script should ran every day from Nov 9 - Nov 13, but it ran just on Nov 9, Nov 10, Nov 12. And I didn't get any notification about the failed execution.
The script itself doesn't use any API, it's pretty basic: reading data in one sheet, doing some calculation and writing to another sheet (talking about the sheets in single Google Sheet file).
If I run the main function manually, it always works.
I'd be very glad to get some ideas what could be wrong. Thanks.
EDIT: Code sample (main function and prototype for Array.includes)
function main(){
var date = new Date();
//var date = new Date(2019, 9, 1); // year, month (zero-indexed!!!), day
//var date = new Date(date.getYear(), date.getMonth()-3); // testing
var currentDay = Utilities.formatDate(date, "CET", "d");
Logger.log('currentDate: ' + Utilities.formatDate(date, "CET", "YYYY-MM-dd HH:mm:ss.S") + ' | currentDay: ' + currentDay);
if (currentDay == 1) {
Logger.log('currentDay is 1st of the month');
date = new Date(date.getYear(), date.getMonth() - 1);
var newCurrentDay = Utilities.formatDate(date, "CET", "d");
}
var monthToCheck = Utilities.formatDate(date, "CET", "MMMM").toUpperCase();
var yearToCheck = Utilities.formatDate(date, "CET", "YYYY");
Logger.log('dateToCheck: ' + Utilities.formatDate(date, "CET", "YYYY-MM-dd HH:mm:ss.S") + ' | monthToCheck: ' + monthToCheck + ' | yearToCheck: ' + yearToCheck);
var firstProjectRow = 7; // first row with the project data
var firstProjectCol = 1; // first column with project data - should contain Tool IDs
var numOfProjectRows = 999; // num of project rows to check (counted from and including var firstProjectRow)
var numOfProjectCols = 21; // num of project columns to check (counted from and including var firstProjectCol the last one contains number of hours for the last service)
var firstProjectHoursCol = 7; // first column with data about project hours (usually PM hours)
// ************* DO NOT EDIT BELOW THIS LINE ************* //
//return;
var indexedFirstProjectHoursCol = firstProjectHoursCol - 1;
var ss = SpreadsheetApp.getActiveSpreadsheet();
//var sheet = ss.getSheets()[3];
var sheetName = monthToCheck + ' ' + yearToCheck;
var sheet = ss.getSheetByName(sheetName);
Logger.log('sheet: ' + sheetName);
var range = sheet.getRange(firstProjectRow, firstProjectCol, numOfProjectRows, numOfProjectCols); // getRange(row, column, numRows, numColumns)
var rangeValues = range.getValues();
//Logger.log('rangeValues: "' + rangeValues);
var toolData = new Array();
var toolIds = new Array();
var toolHours = new Array();
//return;
for (var row in rangeValues) {
Logger.log('row: "' + row);
var clientId = rangeValues[row][0];
var projectId = rangeValues[row][1];
var hoursSum = 0;
// we have Tool ID so it's OK to proceed
if (clientId != "" && projectId != "") {
var clientProjectId = clientId + "-" + projectId;
for (var col in rangeValues[row]) {
var cellValue = rangeValues[row][col];
//Logger.log('col: ' + col + ' value: ' + value);
// get hours sum
if (col >= indexedFirstProjectHoursCol)
hoursSum += typeof cellValue == 'number' ? cellValue : 0;
}
//Logger.log('hoursSum: [' + hoursSum + ']');
var record = {id: clientProjectId, hours: hoursSum};
Logger.log("Data: " + record.id + " : " + record.hours);
// don't yet have a record of clientId-projectId
if (!toolIds.includes(clientProjectId)) {
toolData.push(record);
}
else {
recordIdx = toolIds.indexOf(clientProjectId);
toolData[recordIdx].hours += hoursSum;
}
toolIds = [];
toolHours = [];
toolData.forEach(function(item) {
toolIds.push(item.id);
toolHours.push(item.hours);
});
}
//Logger.log(toolData);
//Logger.log('ROW DONE!');
}
Logger.log('ROWS DONE!');
Logger.log('toolData.length: ' + toolData.length);
toolData.forEach(function(item) {
Logger.log('toolData: ' + item.id + " : " + item.hours);
});
Logger.log('DONE!!!');
// fill the table in the sheet with assigned number of hours
fillTheSheet(sheetName, toolData);
}

Apps Script triggers have always been a bit finicky. But of late they have been far more unreliable than usual (there have been several reports of spurious triggers and other maladies).
In this case, you can avoid using them altogether by leveraging an external service such as cron-jobs.org.
You'll have to refactor your app script project and deploy it as a public Web App with a doPost(e) function. You'd then pass the Web App's url to the external service as a web-hook endpoint that is invoked daily.

Related

Comparing Dates in Google Sheets Script Editor

I am very new to this, so please bear with me. I am trying to get my code to look at each cell in a column, then compare that date to the current date, and if they match send an email. I am aware I will need a loop of some sort to get it to look through the column, but I haven't even gotten that far. I've tried every method I can find online to just get it to compare one cell to the date and send the email.
I tested the email function prior to adjusting it to compare the date, so I know that is working. Put something definitely isn't working...
function sendEmail() {
//Fetch the date
var removalDateRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Expirations").getRange("E2");
var removalDate = removalDateRange.getValue();
var currentDateRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Alerts").getRange("C2");
var currentDate = new Date();
var ui = SpreadsheetApp.getUi();
//Check Date
if (removalDate == currentDate) {
//Fetch the email address
var emailRange =
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Alerts").getRange("B2");
var emailAddress = emailRange.getValue();
//Fetch Item Brand
var itemBrandRange =
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Expirations").getRange("B2");
var itemBrand = itemBrandRange.getValue();
//Fetch Item Name
var itemNameRange =
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Expirations").getRange("A2");
var itemName = itemNameRange.getValue();
//Fetch Item Location
var itemLocationRange =
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Expirations").getRange("H2");
var itemLocation = itemLocationRange.getValue();
// Send Alert Email
var message = 'The ' + itemBrand + ' ' + itemName + ' in ' + itemLocation + ' will expire in 2 months. Please use and replace item.';
// Second Column
var subject = 'Pantry Alert';
MailApp.sendEmail(emailAddress, subject, message);
}
}
EDIT
Okay, I had it working late last night and even got it to loop through, and somehow I've broken it again. I've been looking at answers for hours trying to adjust things to make it work. What am I doing wrong?
function emailAlert() {
// today's date information
var today = new Date();
var todayMonth = today.getMonth() + 1;
var todayDay = today.getDate();
var todayYear = today.getFullYear();
// 2 months from now
var oneMonthFromToday = new Date(todayYear, todayMonth, todayDay);
var oneMonthMonth = oneMonthFromToday.getMonth() + 2;
var oneMonthDay = oneMonthFromToday.getDate();
var oneMonthYear = oneMonthFromToday.getYear();
// getting data from spreadsheet
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Exp");
var startRow = 2; // First row of data to process
var numRows = 500; // Number of rows to process
var dataRange = sheet.getRange(startRow, 1, numRows, 999);
var data = dataRange.getValues();
//looping through all of the rows
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var expireDateFormat = Utilities.formatDate(new Date(row[5]),
'ET',
'MM/dd/yyyy'
);
//email Information
var subject = 'Pantry Item Needs Attention!';
var message1 =
row[6] + ' ' + row[3] + ' of ' + row[2] + ' ' + row[1] + ' will expire on ' + expireDateFormat + '. Item can be found in ' + row[7]
+ '. Please Remove and Replace Item.' +
'\n' + 'Thanks Steve!';
var message2 =
row[6] + ' ' + row[3] + ' of ' + row[2] + ' ' + row[1] + ' will expire on ' + expireDateFormat + '. Item can be found in ' + row[7] +
'. Please ensure item has been replaced, removed from the pantry, and deleted from inventory.' +
'\n' + 'Thanks Steve!'
//expiration date information
var expireDateMonth = new Date(row[5]).getMonth() + 1;
var expireDateDay = new Date(row[5]).getDate();
var expireDateYear = new Date(row[5]).getYear();
//checking for today
if (
expireDateMonth === todayMonth &&
expireDateDay === todayDay &&
expireDateYear === todayYear
) {
ui.alert(message1);
}
}
}
Dates can be frustrating to work with, so consider doing the whole thing (loop, if then statement...) with an integer first and then returning to the date part if you're having trouble. That said, try adjusting the top of your code to look like the following:
var ss =SpreadsheetApp.getActiveSpreadsheet();
var removalDateVal = ss.getSheetByName("Expirations").getRange("E2").getValue();
var removalDate = new Date(removalDateVal);
var currentDateVal = ss.getSheetByName("Alerts").getRange("C2").getValue();
var currentDate = new Date(currentDateVal);
That will give you two date objects. But BEWARE! These dates contain time as well as calendar date so they may not equal each other even when they appear to. Use setHours() to zero out the date as seen below.
currentDate.setHours(0,0,0);
removalDate.setHours(0,0,0);
Other notes, it's best practice to set a variable for a spreadsheet and worksheet as shown by Google here. It makes the code much more readable.

Google app script send email when sheet have no changes

I'm trying to realize email notification at google spreadsheet, when sheet have no changes.
I'm tried to find smth opposite of onEdit() function.
I have email script, it fired by the trigger once at day. It's working nice.
function sendEmailAlert2() {
var ss = SpreadsheetApp.openById("MyID");
var sheetname = ss.getSheetByName("Sheet1");
var Toemail = 'email#gmail.com';
var subject = 'New Entry -' + ss.getName();
var body = 'Your file has a new entry in - ' + sheetname + ' Updated by - ' +
' check file- ' + ss.getUrl();
MailApp.sendEmail(Toemail,subject, body);
}
I need condition to this script when my sheet was no edited on this day.
Maybe someone have any idea?
You can achieve this by implementing a onEdit function which inserts a new time stamp into the sheet every time the file gets edited. And then, in your sendEmailAlert2() function, you can implement an if condition checking if the current day is equal to the day of last time stamp (i.e. the day of the last edit). If there was no edit today, the script email will be sent.
function onEdit(e) {
SpreadsheetApp.getActive().getActiveSheet()
.getRange('A1')
.setValue(new Date());
}
function sendEmailAlert2() {
var ss = SpreadsheetApp.openById("MyID");
var sheetname = ss.getSheetByName("Sheet1").getName();
var Toemail = 'email#gmail.com';
var subject = 'New Entry -' + ss.getName();
var body = 'Your file has a new entry in - ' + sheetname + ' Updated by - ' +
' check file- ' + ss.getUrl();
var NoEditSubject = 'No new Entry in' + ss.getName();
var NoEditBody = 'Your file has no new entry in - ' + sheetname + ss.getUrl();
if(sheetname.getRange('A1').getValue().getDay()==new Date().getDay())
{
MailApp.sendEmail(Toemail,subject, body);
}
else
{
MailApp.sendEmail(Toemail,NoEditSubject, NoEditBody);
}
}
On my side I would recommend you to use the DriveApp service and check if date Last Update is higher than 24h.
function getAlertedIfNoChange(){
var id = 'ID_SHEET';
var file = DriveApp.getFolderById(id);
var date = file.getLastUpdated();
var lastUpdated = date.getTime();
var today = (new Date()).getTime();
var delta = (24 * 60 * 60 * 1000)- (10*60*1000)
if((today - lastUpdated) > delta){
//Fire alert
Logger.log('No update during the last 24h')
}
}
For the 'delta' you have to set it to 24h in milliseconds and by experience I recommend you to remove some minutes, because when you set a trigger to run each day it will not fire at the exact same time. By removing some minutes to the 24h you are sure it will fire an alert.
You program a trigger to run each day and it will be ok.

How to convert cell formula to script

How can I change this simple cell formula to a script that will be apply to the sheet :
=if(D3="Devis";B3+8;(if(D3<>"Devis";"")))
Where :
C column contains the formula
"Devis" is a name presents in the D column
B column contains Dates
Thanks a lot
First you need something that will trigger the script. So you'll need something like a simple onEdit() function, or a change trigger.
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('Sheet1');
var valueOfCellEdited = e.value;
Logger.log('valueOfCellEdited: ' + valueOfCellEdited);
var columnOfCellEdited = e.range.getColumn();
Logger.log('columnOfCellEdited: ' + columnOfCellEdited);
var rowOfCellEdited = e.range.getRow();
Logger.log('rowOfCellEdited: ' + rowOfCellEdited);
if (columnOfCellEdited === 3) {
var valueToGet = sh.getRange(rowOfCellEdited, 4);
var dateToGet = "";
if (valueOfCellEdited === "Devis") { //Date to write is already set to an empty string, so no "Else" needed
var dateInColB = sh.getRange(rowOfCellEdited, 2).getValue();
Logger.log('dateInColB: ' + dateInColB);
Logger.log('typeof dateInColB: ' + typeof dateInColB);
dateToGet = new Date(dateInColB);
var dateInMilliseconds = dateToGet.valueOf();
Logger.log('dateInMilliseconds: ' + dateInMilliseconds);
//60 seconds in a minute = 60 * 1000 = 60000 milliseconds in a minute.
//60 minutes in a hour = 60,000 * 60 = 3,600,000
//24 hours in day 3,600,000 * 24 = 86,400,000
var datePlusEight = dateInMilliseconds + (8 * 86400000);
Logger.log('datePlusEight: ' + datePlusEight);
var newDateIs = new Date(datePlusEight);
Logger.log('newDateIs: ' + newDateIs);
Logger.log('newDateIs: ' + newDateIs.toString());
sh.getRange(rowOfCellEdited, 4).setValue(newDateIs);
};
};
};
Comment out the Logger.log() statements for use. To test, edit a cell in column C, with a date right next to it in the same row, but in column B.
In the spreadsheet, under the Tools menu, choose Script Editor, copy the code and paste it into the script editor.

Google Script says: You have been creating or deleting too many calendars or calendar events in a short time. Please try again later

I am getting this extremely frustrating error. I am in no way creating or deleting too many calendars - at most I've created about 20 and my script is trying to create just one!
I've narrowed down the problem to event.addGuests(). When I remove this line, the event gets added. When I keep it, this is where Google reports the error to be.
I've tried adding
Utilities.sleep(5000);
before event.addGuest() to see if slowing the script down would work, but this doesn't work either.
Here is the whole script. Note, there is only one item in the spreadsheet now
var ACCEPTED = "ACCEPTED";
var PUBLISHED = "PUBLISHED"
var NOT_PUBLISHED = "NOT_PUBLISHED";
function createCalendarEventsFromSheets(){
// Get Calendar Info
var calId = "privatecalendar#group.calendar.google.com";// use default claendar for tests
var cal = CalendarApp.getCalendarById(calId);
// Get Google Sheet Info
var ss = SpreadsheetApp.openById('privatespreadhseet');
var sheet = ss.getSheets()[0];
var range = sheet.getDataRange();
var rows = range.getValues();
Logger.log("Rows found: " + rows.length);
// Iterate through Google Sheets rows and create events
for (i = 1; i < rows.length; i++){
var row = rows[i];
// Gather variables for row
var eventTitle = row[1];
var date = new Date(row[2]);
var location = row[3];
var shiftStart = row[4];
var shiftEnd = row[5];
var eventStart = row[6];
var eventEnd = row[7];
var numberOfKeys = row[8];
var role = row[9];
var notes = row[10];
var accepted = row[13];
var alreadyPublished = row[14]
// If event has been accepted and wasn't published yet, create event
Logger.log("accepted: " + accepted + ", alreadyPublished " + alreadyPublished);
if (accepted == ACCEPTED && alreadyPublished != PUBLISHED){
Logger.log("**** TRUE ****");
// Set up calendar event
var cal_start = new Date(date.getYear(), date.getMonth(), date.getDate(), shiftStart.getHours(), shiftStart.getMinutes());
Logger.log("cal_start: " + cal_start);
var cal_end = new Date(date.getYear(), date.getMonth(), date.getDate(), shiftEnd.getHours(), shiftEnd.getMinutes());
Logger.log("cal_end: " + cal_end);
var description = "Keys Required: " + numberOfKeys + "\n\nRole: " + role + "\n\nEvent Start Time: " + eventStart.getHours() + ":" + eventStart.getMinutes() + ", Event End Time: " + eventEnd.getHours() + ":" + eventEnd.getMinutes() + "\n\nNotes:\n" + notes;
// CREATE EVENT
var event = cal.createEvent(eventTitle, cal_start, cal_end);
event.setDescription(description);
event.setLocation(location);
//Utilities.sleep(5000); // I've tried this, to no avail
event.addGuest("rap#gmail.com"); //error message details point to here
event.addGuest('e.leg#gmail.com');
event.addGuest('ba#hotmail.com');
event.addGuest('r#hotmail.com');
event.addGuest('js#gmail.com');
event.addGuest('w#gmail.com');
event.addGuest('no.abe#gmail.com');
event.addGuest('esth#gmail.com');
event.addGuest('carolin#gmail.com');
event.addGuest('kalan#gmail.com');
event.addGuest('bob#gmail.com');
event.addGuest('rob#gmail.com');
sheet.getRange(i+1, 15).setValue(PUBLISHED);
Logger.log("Event created, starting at: " + event.getStartTime());
}
}
}

Send emails from google spreadsheet - coding suggestions

I am still learning the ropes here. Based on code suggested by other contributors, I put together a script to send reminder emails to consultants who record their time entries using a Google Form. The spreadsheet first imports calendar entries with all the Job information for each consultant. After the calendar entries are imported, if the consultant has not yet recorded their time entry, the following script will send them an email with a reminder to do so:
function sendReminder() {
var rmndrFrom = "XYZ, Inc.";
var myemail = "support#xyz.com";
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Pending");
var numRows = sheet.getLastRow();
var lastCol = sheet.getLastColumn();
var dataRange = sheet.getRange(2, 1, numRows, lastCol); // row 1 is the header row
var sheetData = dataRange.getValues();
for (var i = 0; i < sheetData.length; ++i) {
var row = sheetData[i];
if (row[0]){
var jobNumb = row[0]; // Job Number
var conName = row[2]; // Consultant Name
var conMail = row[3]; // Consultant Email
var jobDate = row[4]; // Date
// format email string
var subject = "Time Entry Reminder: " + conName + " / Job " + jobNumb;
try {
var conMsgH = 'This is a reminder for you to record your time entry for Job #<strong>' + jobNum + '/' + jobDate + '</strong>';
// strip HTML for plain text message
var conMsgP = conMsgH.replace(/\<br\/\>/gi, '\n').replace(/(<([^>]+)>)/ig, "");
// send reminder to consultants
MailApp.sendEmail(conMail, subject, conMsgP, { htmlBody:conMsgH, name:rmndrFrom });
} catch (e) { // error handler
MailApp.sendEmail(myemail, "Error in sending reminder email.", e.message);
}
}
}
}
So basically, this script parses the Pending sheet, if column A has a Job Number, it will send a reminder email to the consultant with that Job Number. However, a single consultant may have several job numbers to their name. I would like the script to send a single email to each consultant with a list of the Job Numbers for which they have to record their time entries.
Thanks in advance for your kind help. Any suggestions on how to optimize the code will also be very much appreciated.
There are a number of ways that you can approach this. One way is to keep a sheet with the consultants emails, names and a list of their job numbers. Load this data into your script, a list of all job ids and the job info. Then filter the job ids based on the consultants list and build your email, or you could just send that list of numbers for a very short script.
Another way is to do all of that sorting per consultant in the code and send out the emails that way. This is the approach I've taken, and I've also made use of the iterative JS functions map, filter and reduce more details at MDN.
The code is posted below, but if you would like to take a look at it attached to a spreadsheet and commented (as well as the functions to build that extra sheet with just the consultants info on it) take a look here.
Below is my iteration of your function. I hope it is helpful for your situation:
var rmndrFrom = "XYZ, Inc.";
var myemail = "me#emailisawesome.com";
var subject = "Time Entry Reminder";
function sendReminder() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Pending");
var numRows = sheet.getLastRow();
var lastCol = sheet.getLastColumn();
var sheetData = sheet.getRange(2, 1, numRows-1, lastCol).getValues();
var cons = sheet.getRange(2,3,numRows-1,1).getValues().reduce(flatten_).filter(getUniqueConsultants_);
cons.forEach(sendEmail_, sheetData);
}
function sendEmail_(consultant) {
var consultantsJobs = this.filter(getJobsForConsultant_, consultant);
var jobList = consultantsJobs.map(buildJobLine_).join("<br>");
try {
var conMsgH = "Hi " + consultant + ",<br>";
conMsgH += "This is a reminder for you to record your time entry for the following jobs:<br><br>";
conMsgH += jobList;
conMsgH += "<br><br>Thank you for your cooperation.";
var conMsgP = conMsgH.replace(/\<br\/\>/gi, '\n').replace(/(<([^>]+)>)/ig, "");
MailApp.sendEmail(consultantsJobs[0][3], subject, conMsgP, {htmlBody:conMsgH, name:rmndrFrom});
} catch (e) {
MailApp.sendEmail(myemail, "Error in sending reminder email.", e.message);
}
}
function buildJobLine_(job) {
return "Job #" + job[0] + " on " + Utilities.formatDate(job[4], Session.getTimeZone(), "MMM dd yyyy");
}
function getJobsForConsultant_(row) {
return row[2] == this;
}
function getUniqueConsultants_(v,i,a) {
return a.indexOf(v) == i;
}
function flatten_(a,b) {
return a.concat(b);
}
I must say that fooby's answer is far beyond my JS skills, I'm sure it will work nicely but I still feel like proposing something different (and simpler from my pov), just for the fun of it ;-)
The main difference with your original script is the sorting of the array that allowed me to detect duplicate names and threat it accordingly. The html composition could be far better for sure but that was not your main request.
Here is the code
function sendReminder() {
var rmndrFrom = "XYZ, Inc.";
var myemail = "support#xyz.com";
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Pending");
var initial = "Hi conName,<BR>This is a reminder for you to record your time entry for the following jobs : <BR><BR><table border = 1 cellpadding = 3 bgcolor='#FFFFBB'><TR><TD>";
var sheetData = sheet.getDataRange().getValues();// simplified code to get the data array
sheetData.shift();//skips the headers
sheetData.sort(function(x,y){
var xp = x[2];// sort on column 3 but you can change here...
var yp = y[2];// sort on column 3 but you can change here...
return xp == yp ? 0 : xp < yp ? -1 : 1;// sort ascending, eventually change here...
});
// Logger.log(sheetData);// sorted
var last = sheetData.length
var i = 1;//index 0 is handled outside the loop
var row = sheetData[0];
var subject = "Time Entry Reminder: " + row[2] + " / Job "
var conMsgH = initial
var msgComponent = makeline_(row)
subject += msgComponent[0]
conMsgH += msgComponent[1]
while (i<last){
if(sheetData[i][2]!=sheetData[i-1][2]||i==last-1){
sendData_(sheetData[i-1][3],sheetData[i-1][2],subject,conMsgH)
var subject = "Time Entry Reminder: " + sheetData[i][2] + " / Job "
var conMsgH = initial;
}
msgComponent = makeline_(sheetData[i])
subject += msgComponent[0]
conMsgH += msgComponent[1]
++i
}
}
function sendData_(conMail,conName,subject,conMsgH){
conMsgH = conMsgH.substr(0,conMsgH.length-8)+'</TABLE>'
conMsgH = conMsgH.replace('conName',conName)
var conMsgP = conMsgH.replace(/<\/tr>/ig, '\n').replace(/<br>/ig, '\n').replace(/(<([^>]+)>)/ig, "")
subject = subject.substr(0,subject.length-2);// remove the last '+ '
// Logger.log(subject)
// Logger.log(conMsgH)
Logger.log(conMsgP)
// Logger.log(conMail)
try{
// send reminder to consultants
MailApp.sendEmail(conMail, subject, conMsgP, { htmlBody:conMsgH, name:rmndrFrom });
} catch (e) { // error handler
// MailApp.sendEmail(myemail, "Error in sending reminder email.", e.message);
}
}
function makeline_(row){
var jobNumb = row[0]; // Job Number
var conName = row[2]; // Consultant Name
var conMail = row[3]; // Consultant Email
var descr = row[1]; // description
var FUS1=new Date(row[4]).toString().substr(25,6)+':00';// get timezone of this event, taking care of daylight savings
var jobDate = Utilities.formatDate(row[4], FUS1, "MMM dd yyyy # hh:mm aaa"); // Date
var subject = jobNumb+' + ';
var conMsgH = 'Job #'+jobNumb + '</TD><TD>' + jobDate + '</TD><TD>' + descr + '</TD></TR><TR><TD>';
return [subject,conMsgH];
}
EDIT : made some improvement in the mail format, used a table to show jobs & dates + removed some bugs ;-) (to be honest, I made this also for my personal use as I am having almost the same use case )