SendEmail function : error because of formulas in blank rows - javascript

I have a sendEmail function which works almost perfectly with my Google Sheet.
The getLastRow function return all my blank rows because they have an hidden formula and I'd like to grab only filled rows.
Here is my code :
var EMAIL_SENT = 'EMAIL SENT';
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = sheet.getLastRow();
// Fetch the range of cells
var dataRange = sheet.getRange(2, 1,numRows-1,20);
// 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 emailAddress = row[7] + "," + row[9] + "," + row[13];
var message = row[18];
var emailSent = row[19];
if (emailSent != EMAIL_SENT) { // Prevents sending duplicates
var subject = 'Un nouvel adhérent vous a été affecté';
MailApp.sendEmail(emailAddress, subject, message);
sheet.getRange(startRow + i, 20).setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
}
Already tried var data = dataRange.getDisplayValues(); instead of var data = dataRange.getValues();without significant result.
Do you have any idea ?
Here is a sample of my sheet :

Solution:
Replace this:
var numRows = sheet.getLastRow();
with:
var numRows = sheet.getRange("F1:F").getDisplayValues().filter(String).length;
The latter will filter out the cells of your sheet that are empty even if they contain a formula.
References:
getDisplayValues()
filter()

Why don't you just add an if statement inside a for loop and check if one of the cells is empty, and break the loop if so.
Your code would look something like this:
var EMAIL_SENT = 'EMAIL SENT';
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = sheet.getLastRow();
// Fetch the range of cells
var dataRange = sheet.getRange(2, 1,numRows-1,20);
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
if(row[6] === "") break;
var emailAddress = row[7] + "," + row[9] + "," + row[13];
var message = row[18];
var emailSent = row[19];
if (emailSent != EMAIL_SENT) { // Prevents sending duplicates
var subject = 'Un nouvel adhérent vous a été affecté';
MailApp.sendEmail(emailAddress, subject, message);
sheet.getRange(startRow + i, 20).setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
}

Related

Google App Script to Group Row data in sheet based on email column

I have google sheet with several projects. The sheet also has names of project leaders that manage these projects. One project leaders can manage up to 5 projects at a go. The sheet looks like this.
I would like to group each row data by column F (email) and send one email to each project lead on the projects they are managing, say for instance Tim Curry will receive a table listing only the projects he leads. My code below is not adequate to accomplish this task and I will appreciate help in modifying it to group the data and send it as HTML.
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 3; // First row of data to process
var numRows = 12; // Number of rows to process
var dataRange = sheet.getRange(startRow, 1, numRows, 12)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
var emails = data.map(function (e) {
return e[5];
})
var uniqueEmails = [...new Set(emails)];
for (var j = 0; j < uniqueEmails.length; j++) {
var subject = "";
var body = "Please see if your project is on course, if not foward this email to PMS admin"
for (var i = 0; i < data.length; i++) {
if (data[i][5] == uniqueEmails[j]){
var row = data[i];
var projectnumber = row[0];
var startdate = row[1];
var enddate = row[2];
var projectname = row[3];
var projectlead= row[4];
var email= row[5];
var lr = sheet.getLastRow();
var tableRangeValues=sheet.getRange(2,1, lr-1,6).getDisplayValues();
var htmlTemplate = HtmlService.createTemplateFromFile("Notify")
htmlTemplate.projectnumber = projectnumber;
htmlTemplate.startdate= startdate;
htmlTemplate.enddate= enddate;
htmlTemplate.projectname= projectname;
htmlTemplate.projectlead=projectlead;
htmlTemplate.email=email;
var htmlForEmail = htmlTemplate.evaluate().getContent();
if (enddate instanceof Date && enddate.getTime()>= new Date(07/14/20).getTime()) {
var emailSent = row[6];
if (emailSent != "Y") {
subject += "Action Required - Project End";
}
}
}
}
if(subject.length > 0){
MailApp.sendEmail({
to: uniqueEmails[j],
cc: "example#gmail.com",
subject: subject,
body: body
});
}
}
}
Notify HTML table Code:
<thead>
<tr>
<th><?=projectnumber?></th>
<th><?=startdate?></th>
<th><?=enddate?></th>
<th><?=projectname?></th>
<th><?=projectlead?></th>
<th><?=email?></th>
</tr>
</thead>
<tbody>
<?tableRangeValues.forEach(r=>{?>
<tr>
<td><?=r[0]?></td>
<td><?=r[1]?></td>
<td><?=r[2]?></td>
<td><?=r[3]?></td>
<td><?=r[4]?></td>
<td><?=r[5]?></td>
</tr>
<?})?>
</tbody>
</table>
Use ... new Set() to retrieve unique emails from the sheet and concatenate the email contents for the rows that correspond to each unique email
Sample:
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 3; // First row of data to process
var numRows = 12; // Number of rows to process
// var numItems = SpreadsheetApp.getActiveSheet().getRange(startRow,1 numRows, sheet.getLastColumn()).getValues();
// var numRows = numItems[0]
var dataRange = sheet.getRange(startRow, 1, numRows, 11)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
var emails = data.map(function (e) {
return e[5];
})
var uniqueEmails = [...new Set(emails)];
for (var j = 0; j < uniqueEmails.length; j++) {
var subject = "";
var body = "Please see if your project is on course, if not foward this email to PMS admin"
for (var i = 0; i < data.length; i++) {
if (data[i][5] == uniqueEmails[j]){
var row = data[i];
var projectnumber = row[0];
var startdate = row[1];
var enddate = row[2];
var projectname = row[3];
var projectlead= row[4];
var email= row[5];
if (enddate instanceof Date && enddate.getTime()>= new Date(07/14/20).getTime()) {
var emailSent = row[6];
if (emailSent != "Y") {
subject += "Action Required - Project: " + projectname + " " + projectnumber + " " ;
}
}
}
}
if(subject.length > 0){
MailApp.sendEmail({
to: uniqueEmails[j],
cc: "admin#example.com",
subject: subject,
body: body
});
}
}
}
UPDATE
If you want to group the rows e.g. to pass them together to a loop in the html template, you can use the Javascript function filter().
Sample
Code.gs
var tableRangeValues;
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 3; // First row of data to process
var numRows = 12; // Number of rows to process
var dataRange = sheet.getRange(startRow, 1, numRows, 12)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
var emails = data.map(function (e) {
return e[5];
})
var uniqueEmails = [...new Set(emails)];
for (var j = 0; j < uniqueEmails.length; j++) {
var subject = "Action Required - Project End";
var body = "Please see if your project is on course, if not foward this email to PMS admin";
tableRangeValues = data.filter(function(row){return (row[5] == uniqueEmails[j]);});
var htmlTemplate = HtmlService.createTemplateFromFile("Notify") ;
var htmlForEmail = htmlTemplate.evaluate().getContent();
body += htmlForEmail;
if(filteredData.length > 0){
Logger.log("body: " + body);
MailApp.sendEmail({
to: uniqueEmails[j],
cc: "example#gmail.com",
subject: subject,
htmlBody: body
});
}
}
}
Notify.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<thead>
</thead>
<tbody>
<?tableRangeValues.forEach(r=>{?>
<tr>
<td><?=r[0]?></td>
<td><?=r[1]?></td>
<td><?=r[2]?></td>
<td><?=r[3]?></td>
<td><?=r[4]?></td>
<td><?=r[5]?></td>
</tr>
<?})?>
</tbody>
</table>
</body>
</html>
Note that you need to declare tableRangeValues as a global variable if you want to use in the html template with scriptlets
Note that if you want to send an email with an htmlBody you have to specify it as such within MailApp.sendEmail()

Google Script Email with CC

I'm having an issue where I want to be able to send an email with CC's based off cell values. i.e. both the main recipient email and CC email are in the spreadsheet in the same row. The code should cycle through each row and send an email to the recipient and attaches the CC to that email.
My code is as follows:
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // Start at second row because the first row contains the data labels
var numRows = 10; // Put in here the number of rows you want to process
// Fetch the range of cells A2:B4
// Column A = Email Address, Column B = First Name
var dataRange = sheet.getRange("A2:C")
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var name = row[0];
var emailAddress = row[1];
var daysAbsent = row[2];
var CC = row[3];
var message = "Hi " + name + ",\n. We see that you have been absent for " + daysAbsent + " days.";
var subject = "Attendance Notification";
MailApp.sendEmail(emailAddress, subject, message,{
cc: CC
});
}
}

Google Sheets email once based on cell values

This script works, except it sends multiple emails, This script check Column F for any value above 0 then sends an email.Column F is where i manually enter data. Trying to get it to only send one email and put a "Email Sent" in column H. Then check if column H has a "Email Sent" if so don't send email again.
Here Is what my sheet looks like
https://docs.google.com/spreadsheets/d/1o5jKMDECIFozrwAQx6EDvPL1NkoMQ1nbpkd6AdS9ULk/edit?usp=sharing
function sendEmails() {
var sSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses"); //get the sheet "Form Responses"
var data = sSheet.getRange(2, 1, sSheet.getLastRow(),sSheet.getLastColumn()).getValues(); //get the values of your table, without the header
var lastRow = sSheet.getLastRow();//gets the last row of entered data
var yourColumn = "H";//set your column
var yourFunction = ("Email Sent");//whatever function or formula or data you want,just don't forget to use lastRow do do the row number
sSheet.getRange(yourColumn+lastRow).setValue(yourFunction);
for (var i = 0; i < data.length; i++) { // For each element of your tab
var row = data[i];
if(row[5] > 0) { // If the person have got at least 1 report outstanding (column E)
var body = "Good day "+ row[0] + " you have " + row[5] + " Internal Audit reports outstanding.";
GmailApp.sendEmail(row[1],"Outstanding Internal Audit Reports", body); // Sending a mail to alert
}
}
}
Try This:
function sendEmailsToTechs() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName("Form Responses");
//var sh=ss.getActiveSheet();
var rg=sh.getDataRange();
var data=rg.getValues();
var hRange=sh.getRange(2,8,sh.getLastRow(),1);
var hValues=hRange.getValues();
for (var i=1;i<data.length;i++){
var row = data[i];
if(row[5] && row[7]!='Email Sent') {
var body = "Good day "+ row[0] + " you have " + row[5] + " Internal Audit reports outstanding.";
GmailApp.sendEmail(row[1],"Outstanding Internal Audit Reports", body);
//Logger.log(body);
hValues[i-1][0]='Email Sent';
}
}
hRange.setValues(hValues);//Puts the values into the spreadsheet all at one time.
}

Google scripts for Spreadsheets (If statements)

I am hoping someone can help me with this issue. I am new to coding. I have a google spreadsheet with a script that pushes out an email. I am attempting to have the script ignore the rows where the script has already sent out an email on.
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('Email Menu')
.addItem('Send Email', 'myFunction')
.addSeparator()
.addToUi();
};
function myFunction() {
var msg = 'Email sent!';
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getActiveSheet();
var range = sheet.getDataRange();
var values = range.getValues();
var EMAIL_SENT = "EMAIL_SENT";
var startRow = 2;
for (i=1; i<values.length;i++) {
//sent = values[i][4]; //added this in to try and skip a row if "X" was in Sent Column
//if (sent ==" ", ) //added this in to try and skip a row if "X" was in Send Column
var email = values[i][0];
var name = values[i][i];
var subject = values[i][2];
var body = values[i][3];
var sent = values[i][5];
if (sent != EMAIL_SENT){
MailApp.sendEmail(email, subject, body);
sheet.getRange(startRow + i,5).setValue(EMAIL_SENT);
SpreadsheetApp.getUi().alert(msg);
}
}}
Google spreadsheet
Implement the following changes in your for loop:
for (i=1; i<values.length;i++) {
var email = values[i][0];
var name = values[i][1]; //not [i][i] !
var subject = values[i][2];
var body = values[i][3];
var sent = values[i][4];
if (sent !== EMAIL_SENT){
sheet.getRange(i+1,5).setValue(EMAIL_SENT);
}
}
Basically get rid of the startRow variable (if you are not using elsewhere) and just use (i+1,5) to place "EMAIL_SENT" in the right column. I have removed parts of your code for the sake of clarity.
Small fix. Replace
sheet.getRange(startRow + i,5).setValue(EMAIL_SENT);
to
sheet.getRange(startRow + i - 1, 6).setValue(EMAIL_SENT);
According to the rest of the script, this should be the most simple fix. This assumes sent column is F (column 6) not E (column 5). Sample sheet including updated script is here.
And! You should change var name = values[i][i]; to var name = values[i][1]; as #jrook pointed. I missed it...

Gmail App Script Add BCC from Spreadsheet

I have created this code to send a template email from Google Spreadsheet. I really need to BCC another recipient. I have checked out Class Gmail App. It didn't quite make sense to me.
var EMAIL_SENT = "EMAIL_SENT";
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menu = [{
name: "Send Email",
functionName: "sendEmails2"
}];
ss.addMenu("Send Email", menu);
}
function sendEmails2() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = 2; // Number of rows to process
// Fetch the range of cells A2:B3
var dataRange = sheet.getRange(startRow, 1, numRows, 3)
// 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 emailAddress = row[0]; // First column
var message = "Hello Team,\n\n" + row[1] + " Please do the Following:..."; // Second column
var emailSent = row[2]; // Third column
if (emailSent != EMAIL_SENT) { // Prevents sending duplicates
var subject = "Team Email";
MailApp.sendEmail(emailAddress, subject, message);
sheet.getRange(startRow + i, 3).setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
}
Check the version of sendEmail(recipient, subject, body, options) method with optional arguments, which can include bcc.
Class GmailApp also has a similar method sendEmail(recipient, subject, body, options), which can include bcc.
Example:
...
var options = {
bcc: 'bccmail0#domain.ext, bccmail1#domain.ext'
};
MailApp.sendEmail(emailAddress, subject, message, options);
...

Categories