Send emails from google spreadsheet - coding suggestions - google-apps-script

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 )

Related

MailApp sendEmail in google sheets

been using MailApp script to automatically send emails from within a google sheet for several years. developed my script from an example i found online and reverse engineered it until i could make it work (i am not a coder!). worked brilliantly for months/years...
but recently it is no longer functioning. i get a daily email summary of errors which states that EMAIL_SENT is not defined, but when running the script in debug mode it times out after the logger.log(user) - so does appear to hang on the EMAIL_SENT variable.
As i say, this has been running brilliantly for many months, but appears to have stopped around july 22nd.
i have included the script below in case it is something simple:
function autoemailES() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("ESautoemail");
//var lastRow = sheet.getLastRow();
var startRow = 2; // First row of data to process
var numRows = sheet.getLastRow() -1; // Number of rows to process
var EMAIL_SENT = 'EMAIL_SENT';
// Fetch the range of cells A2:G
var dataRange = sheet.getRange(startRow, 1, numRows, 30);
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var emailSent = row[7]; // 8th column
if (emailSent !== EMAIL_SENT) { // Prevents sending duplicates
var superMail = row[2]; // 3rd column
var responseNo = row[3]; // 4th column
var user = row[0]; // 1st column
var firstName = row[1]; // 2nd column
var pdfLink = row[5]; // 6th column
var pdfID = row[6]; // 7th column
Logger.log(responseNo) //these logger lines just display the values for each of the variables in debugging
Logger.log(superMail)
Logger.log(user)
Logger.log(pdfLink)
Logger.log(pdfID)
var file = DriveApp.getFileById(pdfID);
var subject = 'IBTPHEM MSF results for ' + user;
var message = 'Dear Educational Supervisor,' + "\n" +
'This is an automated notification that your trainee, ' + firstName + ', has now received a quorate number of MSF responses.'+"\n"+
'The anonymised summary of responses is attached in PDF format, or can be downloaded from the link below.'+"\n" + pdfLink +"\n"+
'Further submissions will be added automatically (the trainee is notified for each), but the pdf will need downloading again'+"\n"+
'Please feel free to share this with your trainee however you see fit - we are now releasing the results via the ES in case there is any feedback that may require debriefing or handling more delicately.'+"\n"+
'Please do let me know of any problems or concerns?'+"\n"+
'Kind Regards,' + "\n" +
'IBTPHEM MSF'
MailApp.sendEmail(superMail,subject, message,{
name: 'IBTPHEM MSF',
replyTo: 'dr_nick#doctors.org.uk',
attachments: [file.getAs(MimeType.PDF)]});
sheet.getRange(startRow + i, 8).setValue(EMAIL_SENT);
SpreadsheetApp.flush();
}
}
}
I would be really grateful for any help to identify my problem.
thanks!
dr_nick
(i cannot easily share the sheet in its entirity as it contains a fair bit of senstive/confidential information - though I suppose I could make an anonymised version if this is necessary/helpful?)
This is the same script with a try catch block. It will only log things if you have an error.
function autoemailES() {
const ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName("ESautoemail");
var startRow = 2;
var EMAIL_SENT = 'EMAIL_SENT';
var dataRange = sheet.getRange(startRow, 1, sheet.getLastRow() - startRow + 1, 30);
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
try{
var row = data[i];
var emailSent = row[7];
if (emailSent !== EMAIL_SENT) {
var superMail = row[2];
var responseNo = row[3];
var user = row[0];
var firstName = row[1];
var pdfLink = row[5];
var pdfID = row[6];
var file = DriveApp.getFileById(pdfID);
var subject = 'IBTPHEM MSF results for ' + user;
var message = 'Dear Educational Supervisor,' + "\n" +
'This is an automated notification that your trainee, ' + firstName + ', has now received a quorate number of MSF responses.' + "\n" +
'The anonymised summary of responses is attached in PDF format, or can be downloaded from the link below.' + "\n" + pdfLink + "\n" +
'Further submissions will be added automatically (the trainee is notified for each), but the pdf will need downloading again' + "\n" +
'Please feel free to share this with your trainee however you see fit - we are now releasing the results via the ES in case there is any feedback that may require debriefing or handling more delicately.' + "\n" +
'Please do let me know of any problems or concerns?' + "\n" +
'Kind Regards,' + "\n" +
'IBTPHEM MSF'
MailApp.sendEmail(superMail, subject, message, {
name: 'IBTPHEM MSF',
replyTo: 'dr_nick#doctors.org.uk',
attachments: [file.getAs(MimeType.PDF)]
});
sheet.getRange(startRow + i, 8).setValue(EMAIL_SENT);
SpreadsheetApp.flush();
}
}
catch(e){
Logger.log('responseNo: %s\nsuperMail: %s\nuser: %s\npdfLink:%s\npdfID: %s',responseNo,superMail,user,pdfLink,pdfID);
Logger.log(e);
}
}
}
Try setting up another spreadsheet and adjust the data so that it doesn't send data to your clients and see if it will generate any errors. Also if you could share a copy of the new spreadsheet as a markdown table that would enable us to test it a little. But be careful about sharing private information

GOOGLE SCRIPT SEND EMAIL WHEN CONDITION MET

Hi I am doing some codes in google script but the output that I am expecting did not happen.
Here is the code that i came up with hope you can help me solve this problem,
function sendEmail() {
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lr = activeSheet.getLastRow();
var dRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("March Cycle").getRange("I6")
var rData = dRange.getValue();
var templateTxt = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Template").getRange(1,1).getValue();
for (var i = 6;i<=lr;i++) {
if(rData = "Touch Course Completed") {
var frstname = activeSheet.getRange(i,4).getValue();
var lstname = activeSheet.getRange(i,3).getValue();
var gradelvl = activeSheet.getRange(i,5).getValue();
var msgbody = templateTxt.replace("{name of student}",(frstname + " " + lstname)).replace("{Gr Lvl}",gradelvl);
MailApp.sendEmail("email add","Test Email",templateTxt);
}
Logger.log(msgbody);
}
}'
below is the data of picture that i wanted to automate.
enter image description here
Thank you
There are a couple of mistakes you have on your code. Let me show you in my comments on this working code.
Code:
function sendEmail() {
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var templateTxt = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Template").getRange(1, 1).getValue();
var lr = activeSheet.getLastRow();
// Compile list of students with "Touch Course Completed" in array before sending
var msgbody = [];
// You need to get the range of all data on column I for it to be optimized
var dRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("March Cycle").getRange("I6:I"+lr);
var rData = dRange.getValues();
// rData is equal to [[Touch Course Completed],[Touch Course Completed],[Not completed]]
// Loop all column I data
rData.forEach(function (data, i){
// Since rData is 2D array, the cell should be accessed by data[0]
if (data[0] == "Touch Course Completed") {
// i is the index of the rData array per loop, so 0 is equivalent to row 6
// We need to offset it to get the correct row
var frstname = activeSheet.getRange(i + 6, 4).getValue();
var lstname = activeSheet.getRange(i + 6, 3).getValue();
var gradelvl = activeSheet.getRange(i + 6, 5).getValue();
// My idea here is optional, but I prefer sending it on 1 email instead of separate per student
// That way, we do it faster and more efficient (note that there are quota/limits on sending mail thus doing this is better)
// But if you need it separate, then do what you did in your script
// I push all message first, then send as bulk outside the loop with join
msgbody.push(templateTxt.replace("{name of student}", (frstname + " " + lstname)).replace("{Gr Lvl}", gradelvl));
}
});
// Send an email only IF there is a "Touch Course Complete" student
if(msgbody)
MailApp.sendEmail("email", "Test Email", msgbody.join("\n"));
}
March Cycle:
Template:
Email:
Note:
Note that the earlier tests did send the email separately. If you need them separate then send it every loop (no need for arrays). But if not, then the code above should be better.
EDIT:
To only send the last row, there can be 2 approach that comes to mind:
Add a column for the identification of the data if it was already sent
You will need to add a column.
If new data doesn't contain "Touch Course Completed", it will not send anything.
If you added multiple rows that contains "Touch Course Completed", all of them will be sent.
Get the last row of msgbody
You will not need to add a column.
If new data doesn't contain "Touch Course Completed", it will send the last row with "Touch Course Completed" that was already sent before.
This will not send multiple rows if ever you added more than 1 "Touch Course Completed"
First approach:
function sendEmail() {
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var templateTxt = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Template").getRange(1, 1).getValue();
var lr = activeSheet.getLastRow();
var msgbody = [];
// Get all headers, check if there is "Already Sent" header
var lc = activeSheet.getLastColumn();
var headers = activeSheet.getRange(1, 1, 1, lc).getValues();
var sentColumn;
if(!headers[0].includes("Already Sent")){
// If not found, add header "Already Sent" right to the last column
sentColumn = lc + 1;
activeSheet.getRange(1, sentColumn).setValue("Already Sent");
}
else {
// If found, get column number of existing "Already Sent" header
sentColumn = headers[0].indexOf("Already Sent") + 1;
}
var dRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("March Cycle").getRange("I6:I"+lr);
var rData = dRange.getValues();
rData.forEach(function (data, i){
if (data[0] == "Touch Course Completed") {
var frstname = activeSheet.getRange(i + 6, 4).getValue();
var lstname = activeSheet.getRange(i + 6, 3).getValue();
var gradelvl = activeSheet.getRange(i + 6, 5).getValue();
// Check if column is already populated with "Y"
var isSent = activeSheet.getRange(i + 6, sentColumn).getValue();
if(isSent != "Y"){
// If column value is not "Y", then add row to the email to be sent, also put "Y" on the column after
msgbody.push(templateTxt.replace("{name of student}", (frstname + " " + lstname)).replace("{Gr Lvl}", gradelvl));
activeSheet.getRange(i + 6, sentColumn).setValue("Y");
}
}
});
// Modified condition for checking array
if(msgbody.length > 0)
MailApp.sendEmail("email", "Test Email", msgbody.join("\n"));
}
Sample data:
Output:
Email:
Note:
The script will automatically add/locate the header, so no need to adjust your sheet manually.
But you can still initialize a column to become "Already Sent" by writing the header name on the first row of the column and write "Y" to those rows you don't want to be sent anymore.

Updating different tab with a script from a pivot table????

I am new to coding scripts in google and come into a problem I can not work through and my script "mentor" is still not that experienced and can't figure it out either.
I am trying to update a different tab on my sheet from a pivot table when I use my script to send out bulked emails.
So the link below will bring you to the dummy sheet built off my real sheet. The 'PM' tab column L is what I am trying to update. It starts at 1 on all jobs. The emails are sent out by the Blue button on the next tab 'Follow up email' (this is just a pivot table so I can always adjust who is getting the emails easily). But I cant figure out how to have it update the 'PM' tab with the button at the same time as the emails go out.
Link to the open shared spreadsheet. Feel free to play around if you can help.
https://docs.google.com/spreadsheets/d/1_hipIj4suI2xMGUrZhMTBDvkQv9Y9O3JRNNQUpSeAP0/edit?usp=sharing
(only got the emails to send properly so far)
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var rows = sheet.getLastRow()
var dataRange = sheet.getRange(2, 1, rows-1, 7);
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var emailAddress = row[1]; // Second column
var message = 'Hello, we have submitted this job ' + row[2] + ' days Ago. ' + row[4] + ' \n\n' + ' -' + row[5];
var subject = row[0]; // First column
MailApp.sendEmail(emailAddress, subject, message);
}
}
I need the button to send out emails on the 'Follow up email' tab and at the same time "email counter" (column L) would get 1 added to it on the 'PM' Tab. This way I can keep track of how many times that job was emailed from the sheet.
Try this code:
function sendEmails() {
var pmSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("PM");
var emailSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Follow up email");
var startRow = 2; // First row of data to process
var rows = emailSheet.getLastRow()
var dataRange = emailSheet.getRange(2, 1, rows-1, 7);
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var emailAddress = row[1]; // Second column
var message = 'Hello, we have submitted this job ' + row[2] + ' days Ago. ' + row[4] + ' \n\n' + ' -' + row[5];
var subject = row[0]; // First column
MailApp.sendEmail(emailAddress, subject, message);
updatePM(pmSheet, emailAddress);
}
}
function updatePM(sheet, email){
var value;
var emails = sheet.getRange("G3:G" + sheet.getLastRow()).getValues();
for (var i = 0; i < emails.length; i++)
if (emails[i][0] == email){
value = sheet.getRange("K" + (i+3)).getValue() + 1
sheet.getRange("K" + (i+3)).setValue(value);
}
}
I changed the way you got the sheets just to be safe, then I just wrote a small function that gets called after the email is sent, which checks the list of emails in the PM sheet and then updates the value in the sent email column.

Googlescript code with namedvalues not working

I am trying to send an automatic email to my students with feedback on the coursework. I have developed a google form with the marking scheme where I enter the student number, but the script doesn't read the named values method.
I have tried to do one by one student numbers and the rest of the code it works.
The code is as follows:
function sendNotification(e){
var s = SpreadsheetApp.getActiveSheet();
var message = "";
var headers =s.getRange(1,1,1,s.getLastColumn()).getValues()[0];
message += "This automated email includes the marks and feedback for
your Main report Assignment.\n\n";
var studentNumber = e.namedValues[headers[3]].toString();
Logger.log("column 3: " + studentNumber);
message += "Student Number: " + studentNumber + "\n\n";
var recipients = studentNumber + "#email.com";
var subject = "Test Email";
MailApp.sendEmail(recipients, subject, message);
}
I want to be able to gather the student email from the google form, that is already saved in column 3 of the spreadsheet that is attached to the form.
Sorry my answer is a little rushed as I am literally leaving. (As is my code.) Normally don't do this, but since you are a teacher willing to make an exception.
Hope the below code helps. (This is how I would accomplish the task you are trying to do.)
function helpingTheTeacher() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("YOUR_SHEET_NAME_HERE");
var values = sheet.getRange(2, 1, sheet.getLastRow(), sheet.getLastColumn()).getValues();
for (var i = 0; i < values.length; i++){
var timeStamp = values[i][0];
var studentName = values[i][1];
var studentNumber = values[i][2];
var studentGrade = values[i][3];
var message = "Hi " +studentName + " your grade is " +studentGrade + "."
var email = studentNumber + "#email.com";
var subject = "ALERT: MESSAGE FROM TEACHER!!!";
MailApp.sendEmail(email, subject, message)
}//END OF FOR LOOP
}//END OF FUNCTION

Test for missing user input, and stop or delay a Google script from running

I have the below Google script running in a Google sheet.
function sendNotification(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var row = sheet.getActiveRange().getRow();
var cellvalue = ss.getActiveCell().getValue().toString();
var emailAdd = "email#yourdomain.com";
if(event.range.getA1Notation().indexOf("G") > -1 && sheet.getRange("G" + row).getDisplayValue() > 999 && emailAdd.length > 1)
{
var rowVals = getActiveRowValues(sheet);
var aliases = GmailApp.getAliases();
Logger.log(aliases);
var bodyHTML,o,sendTO,subject;//Declare variables without assigning a value
o = {};//Create an empty object
bodyHTML = "There has been a new allocation request from " + rowVals.name + " in the " + rowVals.team + " team.<br \> <br \> "
+ "<table border = \"1\" cellpadding=\"10\" cellspacing=\"0\"><tr><th>Issuing Depot</th><th>Delivery Date</th><th>Case Quantity</th></tr><tr><td>"+rowVals.depot+"</td><td>"+rowVals.date+"</td><td>"+rowVals.quantity+"</td></tr></table>"
+ "<br \>To view the full details of the request, use the link below.<br \> <br \>" +
"Allocation Requests"
+"<br \> <br \><i>This is an automated email. Please do not reply to it.<\i>";
o.htmlBody = bodyHTML;//Add the HTML to the object with a property name of htmlBody
o.from = aliases[0]; //Add the from option to the object
sendTO = "email#yourdomain.com";
subject = "Allocation Request - " + rowVals.quantity + " cases on " + rowVals.date,
GmailApp.sendEmail(sendTO,subject,"",o);//Leave the third parameter as an empty string because the htmlBody advanced parameter is set in the object.
};
}
function getActiveRowValues(sheet){
var cellRow = sheet.getActiveRange().getRow();
// get depot value
var depotCell = sheet.getRange("E" + cellRow);
var depot = depotCell.getDisplayValue();
// get date value
var dateCell = sheet.getRange("F" + cellRow);
var date = dateCell.getDisplayValue();
// get quantity value
var quantCell = sheet.getRange("G" + cellRow);
var quant = quantCell.getDisplayValue();
// return an object with your values
var nameCell = sheet.getRange("B" + cellRow);
var name = nameCell.getDisplayValue();
var teamCell = sheet.getRange("C" + cellRow);
var team = teamCell.getDisplayValue();
return {
depot: depot,
date: date,
quantity: quant,
name: name,
team: team
} }
It works fine, but if the person who fills out the spreadsheet doesn't fill out the columns in ascending order, the email that gets sent misses out information.
Is there a way to delay the running of the script until the row (columns B,C,D,E,F & G) has been completed? I've looked at utilities.sleep but not sure where to put it in the script. When I've tried to do so, it doesn't seem to make any difference.
Test the return object for missing values. The way that the following code does this is to convert the object to an array, and then get the length of the array. There should be 4 elements in the array. If the value of any of the variables that go into the object is undefined, then that element will be missing, and therefore the array will have fewer than 4 elements.
function sendNotification() {
var rowVals = getActiveRowValues(sheet);//Returns an object
var testArray = JSON.stringify(o).split(",");//Convert object to an array
Logger.log('length: ' + testArray.length)
if (testArray.length !== 4) {//Object must have 4 elements
Browser.msgBox('There is missing data!');
return; //quit
}
}
function getActiveRowValues() {
var depot = 'something';
var date;//For testing purposes - leave this as undefined
var name = 'the name';
var team = 'team is';
var o = {
depot: depot,
date: date,
name: name,
team: team
}
Logger.log(JSON.stringify(o))
return o;
}
You could improve upon this by highlighting cells with missing data, or determining exactly which piece of data is missing and informing the user.
This is probably not the answer that you want to hear. But I'm not familiar with any trigger that's associated to specific cells. The question has come up many times and the closest we have is the on edit event. I suppose you could check on each on edit to see if the appropriate cells contained appropriate data. Personally, I would prefer a dialog or sidebar for this situation and then I could have all of the power of Javascript in an html environment to help with the form submission process and in the end I'd probably just put a send button there.
I carried on playing around with utilities.sleep and have now got it to work as shown below.
function sendNotification(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var row = sheet.getActiveRange().getRow();
var cellvalue = ss.getActiveCell().getValue().toString();
if(event.range.getA1Notation().indexOf("G") > -1 && sheet.getRange("G" + row).getDisplayValue() > 999)
{
Utilities.sleep(1200000)
var rowVals = getActiveRowValues(sheet);
var aliases = GmailApp.getAliases();
Logger.log(aliases);
var bodyHTML,o,sendTO,subject;//Declare variables without assigning a value
o = {};//Create an empty object
bodyHTML = "There has been a new allocation request from " + rowVals.name + " in the " + rowVals.team + " team.<br \> <br \> "
+ "<table border = \"1\" cellpadding=\"10\" cellspacing=\"0\"><tr><th>Issuing Depot</th><th>Delivery Date</th><th>Case Quantity</th></tr><tr><td>"+rowVals.depot+"</td><td>"+rowVals.date+"</td><td>"+rowVals.quantity+"</td></tr></table>"
+ "<br \>To view the full details of the request, use the link below.<br \> <br \>" +
"Allocation Requests"
+"<br \> <br \><i>This is an automated email. Please do not reply to it.<\i>";
o.htmlBody = bodyHTML;//Add the HTML to the object with a property name of htmlBody
o.from = aliases[0]; //Add the from option to the object
sendTO = "email#yourdomain.com";
subject = "Allocation Request - " + rowVals.quantity + " cases on " + rowVals.date,
GmailApp.sendEmail(sendTO,subject,"",o);//Leave the third parameter as an empty string because the htmlBody advanced parameter is set in the object.
};
}
function getActiveRowValues(sheet){
var cellRow = sheet.getActiveRange().getRow();
// get depot value
var depotCell = sheet.getRange("E" + cellRow);
var depot = depotCell.getDisplayValue();
// get date value
var dateCell = sheet.getRange("F" + cellRow);
var date = dateCell.getDisplayValue();
// get quantity value
var quantCell = sheet.getRange("G" + cellRow);
var quant = quantCell.getDisplayValue();
// return an object with your values
var nameCell = sheet.getRange("B" + cellRow);
var name = nameCell.getDisplayValue();
var teamCell = sheet.getRange("C" + cellRow);
var team = teamCell.getDisplayValue();
return {
depot: depot,
date: date,
quantity: quant,
name: name,
team: team
} }
It now delays the script from running for 1 minute, allowing time for the remaining cells to be completed before the script pull data from them.