Email should NOT be sent when NO data in the selected sheet - google-apps-script

I have a script which pulls data from a defined tab name 'Master' in Google sheet, which is then sending an email triggering at a specific time.
My problem is I don't want script to send blank email if the table in 'Master' is empty. Script should stop sending email.
Otherwise keeps triggering email on mentioned time.
Can anyone please share their insight on this.
function sendEmail() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Master");
var range = sheet.getDataRange();
var recipient = 'email#gmail.com'
var subject = 'Stock Report'
var date = Utilities.formatDate(new Date(), "GMT+1", "dd/MM/yyyy")
var schedRange = sheet.getRange("A1:L21");
var body = '<div style="text-align:center;display: inline-block;font-family: arial,sans,sans-serif">'
body += '<H1>'+ 'Low Stock Report ' +'</H1>';
body += '<H2>'
body += getHtmlTable(schedRange);
body += '</div>';
GmailApp.sendEmail(recipient, subject, "Requires HTML", {htmlBody:body})
//End sendNotification
}

How to check if the table is not empty
If your table is var schedRange = sheet.getRange("A1:L21"); you can check either its empty by retrieving its values and send it only in case a non-empty value is found.
You can implement a boolean variable - as soon as the first non-empty value is found in the table - it will be set to true and an email will be sent.
Sample:
function sendEmail() {
...
var schedRange = sheet.getRange("A1:L21");
var values = schedRange.getValues();
var send = false;
for (var i = 0; i < values.length; i++){
for (var j = 0; j < values[0].length; j++){
var value = values[i][j];
if (value !="" && value !=" "){
return send = true;
}
}
}
if (send == true){
var body = '<div style="text-align:center;display: inline-block;font-family:
...
GmailApp.sendEmail(recipient, subject, "Requires HTML", {htmlBody:body})
}
}
You can also do something like this:
var schedRange = sheet.getRange("A1:L21");
var values = schedRange.getValues();
if (values.flat(2).length>0) {
//Send
var body = '<div style="text-align:center;display: inline-block;font-family:
...
GmailApp.sendEmail(recipient, subject, "Requires HTML", {htmlBody:body})
}
Thanks to the fact that flat() will make a 2D array into a simple array, removing empty elements, we can use it to basically remove all blank values from the array, effectively checking if it's empty or not.

After applying sample code I am not getting email, I have tried both ways with and without data in Master Sheet but no email.I am not sure if i am doing the right way.
Sample:
function sendEmail() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Master");
var range = sheet.getDataRange();
var recipient = 'alislife87#gmail.com'
var subject = 'Stock Report'
var date = Utilities.formatDate(new Date(), "GMT+1", "dd/MM/yyyy")
var schedRange = sheet.getRange("A1:L21");
var values = schedRange.getValues();
var send = false;
for (var i = 0; i < values.length; i++){
for (var j = 0; j < values[0].length; j++){
var value = values[i][j];
if (value !="" && value !=""){
return send = true;
}
}
}
if (send == true){
var body = '<div style="text-align:center;display: inline-block;font-family:
arial,sans,sans-serif">'
body += '<H1>'+ 'Low Stock Report ' +'</H1>';
body += '<H2>'
body += getHtmlTable(schedRange);
body += '</div>';
GmailApp.sendEmail(recipient, subject, "Requires HTML", {htmlBody:body})
}
}

Related

Sending an Email from google sheets only once

The code below works perfectly and sends the email I need it to send on an "OnChange" trigger. However, it sends an email for EVERY checked box in row 7 which is overload, it just needs to send an email for newly checked boxes.
Any advice on how to add a condition in the below code for this?
function sendEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Service');
var data = sheet.getDataRange().getValues();
for (var i = data.length - 1; i >= 1; i--) {
if (sheet.getRange(i,7).isChecked()){
var name = sheet.getRange(i,1).getValue();
var last = sheet.getRange(i,2).getValue();
var body = name + " " + last
var subject = 'New Service Item in Stock'
MailApp.sendEmail('me#mycompany.com', subject, body);
}
}
}
I haven't tried anything because there is nothing I could find online to get me in the right direction.
https://i.stack.imgur.com/cKfLv.png
Try unchecking them after you send them:
function sendEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Service');
var data = sheet.getDataRange().getValues();
for (var i = data.length - 1; i >= 1; i--) {
if (sheet.getRange(i, 7).isChecked() && sheet.getRange(i,8).getValue() != "Sent") {
var name = sheet.getRange(i, 1).getValue();
var last = sheet.getRange(i, 2).getValue();
var body = name + " " + last
var subject = 'New Service Item in Stock'
MailApp.sendEmail('me#mycompany.com', subject, body);
sheet.getRange(i, 8).setValue("Sent");
}
}
}
Performance improvement:
function sendEmails() {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName('Service');
var vs = sh.getRange(2, 1, sh.getLastRow() - 1, sh.getLastColumn()).getValues();
vs.forEach((r, i) => {
if (sh.getRange(i + 2, 7).isChecked() && r[7] == "Sent") {
let body = `${r[0]} ${r[1]}`;
let subject = "New Service Item in Stock";
MailApp.sendEmail('me#mycompany.com', subject, body);
sh.getRange(i, 8).setValue("Sent");
}
});
}

How to send email to only selected emails from google sheet?

new to google script, I have a google script with a button on the sheet which sends out emails from a google sheet, it works fine.
however I want to send emails to those emails that I checkmark manually, I have a column (F) with a checkmark, so I want to checkmark emails then send emails to those emails that have been check-marked only.
I stuck in the completion of the coding for the checkmark part, I appreciate your help.
I have commented out my 2 lines of code for filtering the checkmarks.
the code check for send email quote as well as writes timestamp and email sent note every time email has been sent.
Thanks.
these are columns, one header:
A1: timestamp
B1: email address
C1: Name
D1: Email Sent?
E1: Data emaill sent
F1: check box
function sendEmails(){
var sheet = SpreadsheetApp.openById("mySheetid");
var ss = sheet.getSheetByName("Emails");
var lr = ss.getLastRow();
var EMAIL_SENT = 'Email has been sent';
var messageSubject =
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("BodyTextSheet").getRange(2, 1).getValue();
var messageText = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubjectSheet").getRange(2, 2).getValue();
var quotaLeft = MailApp.getRemainingDailyQuota();
if ((lr-1) > quotaLeft){
Browser.msgBox("you have " + quotaLeft + "left and you're trying to send " + (lr-1) + "emails. Emails were not sent.");
} else{
for (var i = 2; i<=lr; i++){
var currentEmail = ss.getRange(i, 2).getValue();
var currentName = ss.getRange(i, 3).getValue();
var messageBody = messageText.replace("{name}",currentName);
//var selected = ss.getRange("F2:F" + ss.getLastRow()).getValues();
//data = selected.filter(function(r){ return r[6] == true});
MailApp.sendEmail(currentEmail, messageSubject, messageBody);
var EmailSent = ss.getRange(i, 4).setValue(EMAIL_SENT);
var TimeEmailSent = ss.getRange(i, 5).setValue(new Date());
SpreadsheetApp.flush();
}
}
}
This worked out when i tested it. In your code you have BodyTextSheet with the var: messageSubject and viseversa... This is a bit confusing. So you need to change those ranges in this code.
function sendEmails() {
const ss = SpreadsheetApp.openById("mySheetid");
const emailSheet = ss.getSheetByName("Emails");
const bodyText = ss.getSheetByName("BodyTextSheet").getRange(2, 1).getValue()
const subjectText = ss.getSheetByName("SubjectSheet").getRange(2, 2).getValue()
const emails = emailSheet.getDataRange().getValues();
const filterdRows = [];
for (i = 0; i < emails.length; i++) {
const row = emails[i]
if (row[5] == true) {
row.unshift(i+1)
filterdRows.push(row)
}
}
const quotaLeft = MailApp.getRemainingDailyQuota();
if (filterdRows.length > quotaLeft) {
Browser.msgBox(`you have ${quotaLeft} left and you're trying to send ${filterdRows.length} emails. Emails were not sent.`);
} else {
filterdRows.forEach(email => {
const messageBody = bodyText.replace("{name}", email[3]);
MailApp.sendEmail(email[2], subjectText, messageBody);
emailSheet.getRange(email[0], 4, 1, 2).setValues([['Email has been sent', new Date()]])
})
SpreadsheetApp.flush();
}
}
So i made some modifications:
const emails: Get all the emails at once (much quicker)
Loop over the data and check if column F (arrays are 0 indexed so column 5) is true
If is true then add i+1 (rownumber) to the beginning of the array
Then forEach filtertEmail send the mail and set set text and date in the right row (in one call) That is why i pushed the rownumber in the beginning.

Google App Scripts - Auto Sending Emails (specific issue delete row and appendRowL)

I'm trying to use Google App Scripts to send HTML emails for me that I wrote. My code works to the point where I can get the e-mails to send however as you'll see in my code I have 4 different templates that I want to send . . As you'll see I have some code to change the template by 1 to find the right template and dateMath to increase the date for the next e-mail to send.
The issue that I'm running across right now is with deleteRow and appendRow. I have 2 users that I'm trying to e-mail and when I run the code deleteRow and appendRow replaces one of the users with the other. It'll make more sense with my screen shots. Any insight here?
Before Script
After Script
function sendEmails(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = sheet.getDataRange().getValues();
var today = Utilities.formatDate(new Date(), "MDT", "dd/MM/yyyy");
for(var i = 1; i<data.length; i++){
if(data[i][3] !== ""){ //skip if is empty
if(isValidDate(data[i][3])){ //skip if isn't a valid date
var formattedDate = Utilities.formatDate(data[i][3], "MDT", "dd/MM/yyyy");
if(formattedDate == today){ //send email if checkin date is today
if(data[i][4] == 1) {
var template = HtmlService.createTemplateFromFile('Template1');
} else if(data[i][4] == 2){
var template = HtmlService.createTemplateFromFile('Template2');
} else if(data[i][4] == 3){
var template = HtmlService.createTemplateFromFile('Template3');
} else {
var template = HtmlService.createTemplateFromFile('Template4');
}
var email = data[i][2];
var firstName = data[i][0];
var lastName = data[i][1];
template.firstName = firstName;
var subject = "Your Next Steps ";
var bcc = "spencer#kwwestfield.com";
var message = template.evaluate();
GmailApp.sendEmail(email,
subject,
message.getContent(), {
htmlBody: message.getContent(),
bcc: bcc
});
//change the template
//delete the row
sheet.deleteRow(i+1);
//add the row again
var newTemplate = data[i][4] + 1;
var newSend = dateMath(data[i][3], 8);
sheet.appendRow([firstName, lastName, email, newSend, newTemplate]);
}
}
}
}
}
/**
* Does math on dates
* Triggered from functions
* Input: date = the orginal date, d = +- number of days
* Output: a new date
*/
function dateMath(date,d){
var result = new Date(date.getTime()+d*(24*3600*1000));
return result
}
/**
* Figures out if is a data
* Triggered from functions
* Input: d: any
* Output: boolean
*/
function isValidDate(d) {
if ( Object.prototype.toString.call(d) !== "[object Date]" ){
return false;
} else {
return true;
}
}
Because you're deleting a row, the i value no longer corresponds to the same range. For example, you have an array with 5 elements:
[A, B, C, D, E]
If you delete element 0, you'll get
[B, C, D, E]
Now you increment your iterator so i++; // i = 1 and so the next value that you'll operate on is not "B", but instead "C".
You can try yourself by running this
function test() {
var letters = ["A", "B", "C", "D", "E"];
for (var i=0; i<letters.length; i++) {
Logger.log("i : " + i + " || Letter: " + letters[i]);
letters.shift();
}
}
To fix your code, add a row variable that is independent of your array iterator i.
function sendEmails(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = sheet.getDataRange().getValues();
var today = Utilities.formatDate(new Date(), "MDT", "dd/MM/yyyy");
var row = 2; // Starting at row 2
for(var i = 1; i<data.length; i++){
if(data[i][3] !== ""){ //skip if is empty
if(isValidDate(data[i][3])){ //skip if isn't a valid date
var formattedDate = Utilities.formatDate(data[i][3], "MDT", "dd/MM/yyyy");
if(formattedDate == today){ //send email if checkin date is today
if(data[i][4] == 1) {
var template = HtmlService.createTemplateFromFile('Template1');
} else if(data[i][4] == 2){
var template = HtmlService.createTemplateFromFile('Template2');
} else if(data[i][4] == 3){
var template = HtmlService.createTemplateFromFile('Template3');
} else {
var template = HtmlService.createTemplateFromFile('Template4');
}
var email = data[i][2];
var firstName = data[i][0];
var lastName = data[i][1];
template.firstName = firstName;
var subject = "Your Next Steps ";
var bcc = "spencer#kwwestfield.com";
var message = template.evaluate();
GmailApp.sendEmail(email,
subject,
message.getContent(), {
htmlBody: message.getContent(),
bcc: bcc
});
//change the template
//delete the row
sheet.deleteRow(row);
row--; // Deleted a row
//add the row again
var newTemplate = data[i][4] + 1;
var newSend = dateMath(data[i][3], 8);
sheet.appendRow([firstName, lastName, email, newSend, newTemplate]);
}
}
}
row++; // Go to the next row
}
}

Google Form Send Email to Owner after Response Submission

I am just starting Google Forms. I need to email the form owner (myself and some others) a response as soon the the user submit the data. I need the data in the email which would include fields and their values that were submitted by the user as soon as they submit the form.
I am unable to use add-on as per my google account settings via my employer where add-on are blocked.
I am exploring app scripts but with little success as I am very new. As there some sample codes to help me get started with create a basic script to send email.
I have the following Code:
function sendFormByEmail(e)
{
var email = "ownersemail#host.ca";
var s = SpreadsheetApp.getActiveSheet();
var headers = s.getRange(1,1,1,s.getLastColumn()).getValues()[0];
var message = "";
var subject = "New Hire: ";
for(var i in headers)
message += headers[i] + ': '+ e.namedValues[headers[i]].toString() + "\n\n";
subject += e.namedValues[headers[2]].toString() + " - starts " + e.namedValues[headers[15]].toString();
MailApp.sendEmail(email, subject, message);
}
Then I added this script in the form trigger like so:
I tried submitting the form but nothings heppens. How do I know that the script ran or there was a problem?
If I try to run this in the script editor :
It gives me an error :
TypeError: Cannot call method "getRange" of null. (line 7, file "Code")
Update
I tested the email functionality and it worked. So the problem has to be in Spread Sheet value retrieval.
function sendFormByEmail(e)
{
var email = "ownersemail#host.ca";
MailApp.sendEmail(email, "Test", "Test");
}
I also created a excel file on my google drive that holds these response from google form
Final Solution
function testExcel() {
var email = "ownersemail#host.ca";
var s = SpreadsheetApp.openById("GoogleDocsID");
var sheet = s.getSheets()[0];
var headers = sheet.getRange(1,1,1,sheet.getLastColumn()).getValues()[0];
var datarow = sheet.getRange(sheet.getLastRow(),1,1,sheet.getLastColumn()).getValues()[0];
var message = "";
for(var i in headers)
{
message += "" + headers[i] + " : " + datarow[i] + "\n\n";
//Logger.log(message);
}
MailApp.sendEmail(email, "Submitted Data Test", message);
}
Here is a shell for you to start with. I use this code for a very similar reason. This shell includes creating a Google Doc from template and adding data from the sheet into that Doc. You can use similar methods to set variables and add them into the email. I use an html template file(s) to manage exactly what is being sent each time.
The merge portion checks through the Doc (you can set it to look through html file) and finds my tags using RegEx; structed as so: <<columnHeader>>. In this way, You have a consistent template that replaces those tags with the data, in that column, for that row. Modify to your needs as you see fit.
This also displays the progress of the merge. That way, it won't repeat your emails/ merge.
NOTE: There are several data points missing as I removed the personal information; it won't run straight from this sample. You will have to add your document IDs, correct for variable placement, etc.
function mergeApplication() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("");
var formSheet = ss.getSheetByName("");
var lastRow = formSheet.getLastRow();
var lastColumn = sheet.getMaxColumns();
function checkAndComplete() {
var urlColumn = lastColumn;
var checkColumn = (urlColumn - 1);
var checkRange = sheet.getRange(2, checkColumn, (lastRow - 1), 1);
var check = checkRange.getBackgrounds();
var red = "#ff0404";
var yellow = "#ffec0a";
var green = "#3bec3b";
for (var i = 0; i < check.length; i++) {
if (check[i] == green) {
continue;
} else {
var statusCell = sheet.getRange((i+2), checkColumn, 1, 1);
var urlCell = sheet.getRange((i+2), urlColumn, 1, 1);
var dataRow = sheet.getRange((i+2), 1, 1, (lastColumn - 2));
function mergeTasks() {
function docCreator() {
var docTemplate1 = DriveApp.getFileById("");
var docTemplate2 = DriveApp.getFileById("");
var folderDestination = DriveApp.getFolderById("");
var clientName = sheet.getRange((i+2), 2, 1, 1).getValue();
var rawSubmitDate = sheet.getRange((i+2), 1, 1, 1).getValue();
var submitDate = Utilities.formatDate(rawSubmitDate, "PST", "MM/dd/yy");
var typeCheck = sheet.getRange((i+2), (checkColumn - 1), 1, 1).getValue();
if (typeCheck == "Type 1") {
var docToUse = docTemplate1;
var emailBody = HtmlService.createHtmlOutputFromFile("").getContent();
} else {
var docToUse = docTemplate2;
var emailBody = HtmlService.createHtmlOutputFromFile("").getContent();
}
var docName = "" + clientName + " - " + submitDate;
var docCopy = docToUse.makeCopy(docName, folderDestination);
var docId = docCopy.getId();
var docURL = DriveApp.getFileById(docId).getUrl();
var docToSend = DriveApp.getFileById(docId);
var docInUse = DocumentApp.openById(docId);
var docBody = docInUse.getBody();
var docText = docBody.getText();
function tagReplace() {
var DOBCell = sheet.getRange((i+2), 3, 1, 1);
var rawDOB = DOBCell.getValue();
if (rawDOB !== "") {
var DOB = Utilities.formatDate(rawDOB, "PST", "MM/dd/yy");
} else {
var DOB = ""
}
var taggedArray = docText.match(/\<{2}[\w\d\S]+\>{2}/g);
var headerArray = sheet.getRange(1, 1, 1, (lastColumn - 2)).getValues();
var dataArray = dataRow.getValues();
dataArray[0][2] = DOB;
var strippedArray = [];
function tagStrip() {
for (var t = 0; t < taggedArray.length; t++) {
strippedArray.push(taggedArray[t].toString().slice(2, -2));
}
}
function dataMatch() {
for (var s = 0; s < strippedArray.length; s++) {
for (var h = 0; h < headerArray[0].length; h++) {
if (strippedArray[s] == headerArray[0][h]) {
docBody.replaceText(taggedArray[s], dataArray[0][h]);
}
}
}
docInUse.saveAndClose();
}
tagStrip();
dataMatch();
}
function emailCreator() {
var emailTag = sheet.getRange((i+2), (checkColumn - 9)).getValue();
var emailSubject = "" + clientName;
MailApp.sendEmail({
to: emailTag,
subject: emailSubject,
htmlBody: emailBody,
attachments: [docToSend.getAs(MimeType.PDF)],
replyTo: "",
});
}
tagReplace();
statusCell.setBackground(yellow);
emailCreator();
urlCell.setValue(docURL);
}
statusCell.setBackground(red);
docCreator();
statusCell.setBackground(green);
}
mergeTasks();
}
}
}
checkAndComplete();
}

Google Apps Script - Send Email with Edit Link

I am creating a script that sends a custom email right after you send the form. It works perfectly but I wanted it to work also when I edit the form it sends a automatic email with the changes made.
function Initialize() {
var triggers = ScriptApp.getProjectTriggers();
for (var i in triggers)
ScriptApp.deleteTrigger(triggers[i]);
ScriptApp.newTrigger("SendGoogleForm")
.forSpreadsheet(SpreadsheetApp.getActiveSpreadsheet())
.onFormSubmit().create();
}
function SendGoogleForm(e) {
var s = SpreadsheetApp.getActiveSheet();
var headers = s.getRange(1,1,1,s.getLastColumn()).getValues()[0];
var message = "Please paste this!\n\n";
var subject = "Email: ID: ";
var email = "";
//
var form = FormApp.openById('*******************'); //this is the ID in the url of your live form
var formResponses = form.getResponses();
//
for (var i = 0; i < formResponses.length; i++) {
var formResponse = formResponses[i];
}
for(var i in headers) {
if ( e.namedValues[headers[i]].toString() != "") {
message += headers[i] + ': '+ e.namedValues[headers[i]].toString() + "\n";
}
}
subject += e.namedValues[headers[2]].toString();
email += e.namedValues[headers[1]].toString();
message += "Edit Link: " + formResponse.getEditResponseUrl();
MailApp.sendEmail(email, subject, message);
}
I've found a script that may help to send a email when it is edited but I am not sure how I can implement it(found it in google docs forums https://productforums.google.com/forum/#!topic/docs/-guIl5QlvKk):
function checkResponse() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Form Responses 1');
var lastRow = s.getLastRow();
var range = s.getRange('A' + lastRow + ':C' + lastRow);
var notes = range.getNotes();
var values = range.getValues();
var changedFlag = null;
var body = '';
for (var i = 0; i < notes[0].length; i++ ) {
if ( notes[0][i] == 'Responder updated this value.' ) {
changedFlag = true;
// We know only to send the changed values
// Add changed value to email msg
body += values[0][i];
// May also want to clear the note as it remains after future edits of other values as well
s.getRange(lastRow, i).clearNote();
}
}
if ( !changedFlag ) {
// Email the whole row of values
for (var i = 0; i < values.length; i++) {
body += values[0][i];
}
}
GmailApp.sendEmail(recipient, subject, body)
}