I am having problems with a script that I wrote. It isn't throwing any errors however I am not receiving the emails. The goal of this script is to send an email if column G has value Y.
Column A = Organization Name
Column C= Days Aging
Column F= Email (where the email should be sent)
Column G= Status (where the Y will be)
function CheckPastDueAccounting() {
//Fetch Accounting Spreadsheet with past due accounts
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var lrow= ss.getLastRow();
for(var i=2;i<=lrow;i++);
var Status =ss.getRange(i,7);
var value = Status.getValue();
if (value == 'Y') {
var DaysAgingColumn = ss.getRange(i,3);
var DaysAging = DaysAgingColumn.getValue();
// Fetch Organization Name
var Organizations = ss.getRange(i,1);
var org = Organizations.getValue();
// Fetch the email address
var emailRange = ss.getRange(i,6);
var emailAddress = emailRange.getValue();
// Send Alert Email.
var message = 'Hi. Your account is past due ' + DaysAging + ' days.'; // Second column
var subject = 'Past Due Accounting Alert: ' + org;
GmailApp.sendEmail(emailAddress, subject, message);
}
}
Issue:
You should be enclosing the statements you want to run in loop between { }. Otherwise, the for loop is just incrementing the variable i and not running any of your desired code.
Moreover, after the for loop ends, the variable i is higher than the last row with content (lrow) which means that all calls to .getRange(i, columnIndex) refer to empty cells. Because of this, no email is sent, and no error is shown.
Solution:
function CheckPastDueAccounting() {
// Code before for loop starts
for(var i=2; i<=lrow; i++) {
// Code to run in loop (iterate through all rows with content)
}
// Code to run after for loop ends
}
Improving code:
Calling getRange(row, column) for individual cells, for each row in a for loop, is not the best way to iterate through rows with data.
It's much better to first (1) get all the values in the desired range, with getRange(row, column, numRows, numColumns) and getValues(), and then (2) iterate through the resulting 2D array, like this:
function CheckPastDueAccounting() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var lrow= ss.getLastRow();
var firstRow = 2;
var values = ss.getRange(firstRow, 1, lrow - firstRow + 1, 7).getValues();
for (var i = 0; i < values.length; i++) {
var row = values[i];
var value = row[6];
if (value == 'Y') {
var DaysAging = row[2];
var org = row[0]; // Fetch Organization Name
var emailAddress = row[5]; // Fetch the email address
var message = 'Hi. Your account is past due ' + DaysAging + ' days.';
var subject = 'Past Due Accounting Alert: ' + org;
GmailApp.sendEmail(emailAddress, subject, message); // Send Alert Email.
}
}
}
Reference:
for loop
getRange(row, column, numRows, numColumns)
Related
Given
Column G is having the expected return date(i.e. reminder date).
Column J is having email address stored
Column L is having return status
Need
Need to send the reminder email(email address Col J) on the reminder date(COl G) if the Column L (Return_Status) is blank.
I already have a code written, can't figure out the exact issue why it is not working.
var EMAIL_SENT = "EMAIL_SENT";
function sendEmails() {
var today = new Date().toLocaleDateString(); // Today's date, without time
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = 999; // Number of rows to process
// Fetch the range of cells A2:B999
//var dataRange = sheet.getRange(startRow, 1, numRows, 999)
//var dataRange= sheet.getRange("Form Responses 1!A1:L");
var dataRange= sheet.getRange(startRow,numRows)
// 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 = "dummy#gmail.com";
var subject = "RC Reminder # "+row[3];
var message = "Reminder for "+row[4]+" RC of vehicle"+row[3]+" handed over to "+row[5]+" against "+row[2]+" on "+row[0];
var emailSent = row[10];
var reminderDate = new Date(row[6]).toLocaleDateString();
if (reminderDate != today) // Skip this reminder if not for today
continue;
if (emailSent != EMAIL_SENT) { // Prevents sending duplicates
MailApp.sendEmail(emailAddress, subject, message,{name:'Sam'});
sheet.getRange(startRow + i, 11).setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
}
In this line of code you're just getting the value of one cell
var dataRange= sheet.getRange(startRow,numRows)
getRange(row, column)
You should change it to:
var dataRange = sheet.getRange(startRow, 1, sheet.getLastRow()-1,sheet.getLastColumn())
getRange(row, column, numRows, numColumns)
This way you will only iterate through the values that are in your sheet and you will not work through empty data. If you have several sheets in your spreadsheet try consider to work with the one you have the data, accesing to the right one like this:
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('NAME OF YOUR SHEET');
getSheetByName(name)
I am using this spreadsheet Here
I am trying to send an email to a specific recipient "email#myemail.com" when there are 3 days left for a specific date.
Each row represents a person and I want the email sent to send me an email for each person whose date of offboarding - date of today is exactly 3 days.
This is my output: Email received
This is the script i am using:
function Offboarding_Reminder() {
// get the spreadsheet object
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
// set the first sheet as active
SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[0]);
// fetch this sheet
var sheet = spreadsheet.getActiveSheet();
// figure out what the last row is
var lastRow = sheet.getLastRow();
// the rows are indexed starting at 1, and the first row
// is the headers, so start with row
var startRow = 2;
// grab column 11 (the 'days left' column)
var range = sheet.getRange(2,12,lastRow-startRow+1,1 );
var numRows = range.getNumRows();
var days_left_values = range.getValues();
// Now, grab the user name column
range = sheet.getRange(2, 1, lastRow-startRow+1, 1);
var reminder_name = range.getValues();
var warning_count = 0;
var msg = "";
// Loop over the days left values
for (var i = 0; i < numRows - 1; i++) {
var days_left = days_left_values[i][0];
if(days_left == 3) {
// if it's exactly 3, do something with the data.
var user_name = reminder_name[0][i];
msg ="Reminder:"+reminder_name+" offboarding is due in "+days_left+" days.\n";
warning_count++;
}
}
if(warning_count) {
MailApp.sendEmail("email#myemail.com","Reminder Offboarding",msg)
}
};
Two things I don't understand:
Why is my sent email giving me all the user emails instead of just the ones that have days_left == 3?
To automate this script, once it works, I should just add a trigger to the appscript?
Thanks for the help!
I actually revised your code since it wasn't optimized. It had a lot of redundant and unnecessary lines.
Here are the changes:
fetched the range from user column to days_left column once, not separately
created array to store users matching the condition
formatted the email to look better.
Code:
function offboardingReminder() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
// set active sheet to first sheet
spreadsheet.setActiveSheet(spreadsheet.getSheets()[0]);
var sheet = spreadsheet.getActiveSheet();
// figure out what the last row is
var lastRow = sheet.getLastRow();
var startRow = 2;
// grab all data from user to days left
var range = sheet.getRange(startRow, 1, lastRow - startRow + 1, 12);
var values = range.getValues();
var users = [];
// loop all data per row
values.forEach(function(row) {
// if days left is 3
if(row[11] == 3) {
// add user if 3 days left
users.push(row[0]);
}
});
// if users has elements
if(users) {
// Formatted the message as html to look nicer
var message = "<html><body><h1>Reminder!!!</h1><p>The following user/s offboarding is due in 3 days:</p>";
// created bulleted list for list of users
var emails = "<ul>";
users.forEach(function(user){
emails = emails + "<li>" + user + "</li>";
});
emails += "</ul>";
message = message + emails + "</body></html>";
MailApp.sendEmail("email#myemail.com", "Reminder Offboarding", "", {htmlBody: message, noReply: true});
}
}
Sample data:
Sample output:
Note:
You can calculate the days left in script instead, but if it is already needed in sheets, then reusing it would be fine.
I populated the days left column via formula =DAYS(C2,TODAY()) for row 2 (dragged for other rows)
I need to send an email from my sheets based on the first Column inputs (email ID's) and (which is dynamic row and gets updated based on the time). how can I return only A2:A length using the google script. Also, how can I do it in excel as well?
var formattedDate = Utilities.formatDate(new Date(), "GMT-6", "'Date: ' yyyy-MM-dd ' Time: ' HH:mm:ss ' CDT'");
var EMAIL_SENT = 'Email Success ! '+ "\n\n" + formattedDate;
/**
* Sends non-duplicate emails with data from the current spreadsheet.
*/function sendEmails2() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Script (Beta)").activate();
var startRow = 2; // First row of data to process
var numRows = 120; // Number of rows to process
// Fetch the range of cells 'A' columns
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 = row[1]; // Second column
var emailSent = row[2]; // Third column
if (emailSent !== EMAIL_SENT) { // Prevents sending duplicates
var subject = '[Auto] The Process Has Not Yet Been Started';
if(emailSent =='')
break;
MailApp.sendEmail(emailAddress, subject, message, {htmlBody: message,
cc: 'abc#xyz.com',
bcc:'cde#xyz.com'});
sheet.getRange(startRow + i, 3).setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is interrupted
Logger.log(sendEmails2);
SpreadsheetApp.flush();
}}}
function A2A(col,sh,ss) {
var ss=ss || SpreadsheetApp.getActive();
var sh=sh || ss.getActiveSheet();
var col=col || 1;
return sh.getRange(2,col,sh.getLastRow()-1,1).getValues().filter(String).length;
}
To get the last row of the 'A' column you can get first all the values of the row:
var dataRange = sheet.getRange('A:A').getValues();
getValues() returns a two-dimensional array of values, indexed by row,
then by column
Since we have already set the column to be A we will need only one for to loop through the array:
var lastDataPosition = 0; // Create variable to set the last row of the column that has data
for (var i = 0; i < dataRange.length; ++i) {
if (dataRange[i] != '') { // Check if the array position has data
lastDataPosition = i + 1 // If the position has data in it -> assign that position to 'lastDataPosition' as the last one
}
}
*We add +1 to the array position since the first cell of the sheet is 1 and the first position of the array is 0
This is how you get the last row with data even if there are some blank cells in the middle.
For excel, I recommend you to create a new question with the proper tags.
I've been using Google form to collect information to a workbook, then using that information to send customized emails back to the sender. I am a noob at coding, but I have managed to cobble together enough for this task. My problem is that, when it comes to a blank email cell it stops. I've tried to put in a loop to make it skip, with no success. Any help would be appreciated. This is the program I am using without a loop.
// This constant is written in column C for rows for which an email
// has been sent successfully.
var EMAIL_SENT = "EMAIL_SENT";
function sendEmails2() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = 2000; // Number of rows to process
// Fetch the range of cells A2:B2001
var dataRange = sheet.getRange(startRow, 1, numRows, 2001)
// 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 = row[1]; // Second column
var emailSent = row[2]; // Third column
if (emailSent != EMAIL_SENT) { // Prevents sending duplicates
var subject = "Bounce Reply";
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();
}
}
}
You can use string.match() function to determine if a cell is empty or not. Since you are looking for email address you might as well see if the cell has '#' sign. If not exclude the cell and continue on to the next cell like so:
if (emailAddress.match('#') === null){
continue; // skip this iteration of the loop and go to the next one
}
Your final code will look like:
var EMAIL_SENT = "EMAIL_SENT";
function sendEmails2() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = 2000; // Number of rows to process
// Fetch the range of cells A2:B2001
var dataRange = sheet.getRange(startRow, 1, numRows, 2001)
// 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
if (emailAddress.match('#') === null){
continue; // skip this iteration of the loop and go to the next one
};
var message = row[1]; // Second column
var emailSent = row[2]; // Third column
if (emailSent != EMAIL_SENT) { // Prevents sending duplicates
var subject = "Bounce Reply";
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();
}
}
}
Edit
To check if something is empty or not you can try something like this.
if (message.match(/[a-zA-Z0-9_]/) === null) { //" /[a-zA-Z0-9_]/ " is regex to match any and every alphanumeric letter including underscore
continue; // skip this iteration of the loop and go to the next one
}
You can find more details on function match and regex here.
Hope that helps!
I have a google script I am using and I was wanting to insert the current row I am working with into a value which will be re added into a formula I set back in the spreadsheet. I want to do this in case someone resubmits the same data, so they are emailed the changes. At the moment this script is just a proof of concept for me, so I can then go on and code the entire process.
The problem I am having with the script is with the value currentRow. It seems to get the value of the first pass and then does not change it, which is problematic when you have more than one entry to process.
I use the value currentRow in the variable RESEND_EMAIL, so it can generate a formula which looks to see if the two columns are alike (I am going to use the date time stamp and copy it in just the first time to column D (also known as var resend[4]).
This way I can set the script to run automatically and check if this column is set as "Yes", in which case it will send and email again and then change the a value (shown here as email address - but I will use the timestamp instead) so that the formula RESEND_EMAIL changes resend back to "No".
How do I always ger the correct row Number in the value currentRow?
// This constant is written in column C for rows for which an email
// has been sent successfully.
var EMAIL_SENT = "EMAIL_SENT";
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, 6)
// 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 = row[1]; // Second column
var emailSent = row[2]; // Third column
var resend = row[4];
var extension = row [5];
var currentRow = sheet.getActiveRange().getRow();
var RESEND_EMAIL = "=IF(A"+ currentRow +"=D" + currentRow +",\"No\",\"Yes\")";
if (emailSent != EMAIL_SENT) { // Prevents sending duplicates
var subject = "Sending emails from a Spreadsheet";
MailApp.sendEmail(emailAddress, subject, message);
sheet.getRange(startRow + i, 3).setValue(EMAIL_SENT);
sheet.getRange(startRow + i, 5).setFormula(RESEND_EMAIL);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
if (resend == "Yes") { // Prevents sending duplicates
var subject = "Sending emails from a Spreadsheet";
message = message + " " + extension;
MailApp.sendEmail(emailAddress, subject, message);
sheet.getRange(startRow + i, 4).setValue(emailAddress);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
}
As shown in your code i variable value becomes the current row.
...
var currentRow = startRow + i;
...