I'm currently trying to write an Apps Script function that sends an email when its Google Sheet is edited. My current problem is that the function only sends one column of data and I need it send both the date and amount of inventory. This is what I currently have, I'm thinking that maybe I need a nested loop in order for the function to print both columns. Any help would be great!
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
// Fetch the range of cells A2:B3
var dataRange = sheet.getRange(2, 1, 4, 2);
// Fetch values for each row in the Range.
var data = dataRange.getValues();
//var data = dataRange.getSheetValues();
for (i in data) {
var row = data[i];
var message = row[1]; // Second column
var subject = "Inventory update";
MailApp.sendEmail("test#test.com", subject, message);
}
}
In the above code values of your cells from range "A2:B5" are stored in an array of array format in variable data i.e
var data = dataRange.getValues(); //This code results in the below
data = [["A2","B2"],
["A3","B3"],
["A4","B4"],
["A5","B5"]]
Where A2 corresponds to the value of cell A2, B2 to cell B2 and so on. To access the first row you would the following:
data[0] => ["A2","B2"]
Note: data[0] returns another array containing all the elements on row 1. Also, note the numbering starts from 0 not form 1
Now to access the first element of the first row, you would do the following:
data[0][0] => "A2"
So similarly in your above, code when you do this
var row = data[i]; where i = 0
row => ["A2","B2"]
Hence to get first column you would do
row[0] => "A2"
to get the second column you would do
row[1] => "B2"
Modify your message variable to look like this
message = message + row[0] +","+row[1]"\n"
Where in your appended the new row to the previous message with a comma delimiter and \n new line feed.
As of now, your mail app sends 1 email per each row. However, I don't believe that is the intended behavior. Below is your modified code
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
// Fetch the range of cells A2:B3
var dataRange = sheet.getRange(2, 1, 4, 2);
// Fetch values for each row in the Range.
var data = dataRange.getValues();
//var data = dataRange.getSheetValues();
var message ="" //empty string
//Loop through each row and form CSV data
for (var i=0;i<data.length;i++) { //edited because:https://stackoverflow.com/questions/500504/why-is-using-for-in-with-array-iteration-a-bad-idea
var row = data[i];
message = message + row[0] +","+row[1]"\n"
}
// Send one email with all the rows
var subject = "Inventory update";
MailApp.sendEmail("test#test.com", subject, message);
}
The initial question did not insist on a coded solution.
Therefore, your exact usecase can be done without code, using this Gsheet addon:
https://reactor.isvery.ninja/
see the tutorial video in the bottom (send email from spreadsheet)
Related
I have a code that sends out emails to a specific email address (in this case lets say its myemailaddress#gmail.com) based on the values in my Google Sheet tab named 'Send Emails [Team A]'. It runs when I click on the menu item 'To Team A'. The code is sending out emails fine, however it also runs on rows that are blank, thus sending out blank emails to myemailaddress#gmail.com. The sheet will be updated from time to time which is why I did not limit the range until a specific row. Is there a way to make the code run only on rows that are not blank?
Here's the code that I'm using:
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu("Send Emails")
.addItem("To Team A", "toTeamA")
.addToUi();
// This constant is written in column C for rows for which an email
// has been sent successfully.
var EMAIL_SENT = "Email Sent";
function toTeamA() {
var sheet = SpreadsheetApp.getActive().getSheetByName("Send Emails [Team A]");
var startRow = 2; // First row of data to process would be '2' because the first row is header
var numRows = 1000; // Number of rows to process
var dataRange = sheet.getRange('A2:D') // Gets the data range
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var emailAddress = "myemailaddress#gmail.com";
var subject = row [1]; // Second column
var message = row [0]; // First column
var emailSent = row [2]; // Third column
if (emailSent != EMAIL_SENT) { // Prevents sending duplicates
MailApp.sendEmail(emailAddress, subject, message);
sheet.getRange(startRow + i, 3).setValue(EMAIL_SENT);
// Makes sure cell is updated right away with "Email Sent" in case the script is interrupted
SpreadsheetApp.flush();
}
}
}
Any help is much appreciated!
Sure, assuming your rows are empty, e.g. rows 1-100 contain content, whereas 101 onwards it is empty. You can find the last row with data using sheet.getLastRow(), see docs.
So instead of doing sheet.getRange("A2:D") you can do sheet.getRange("A2:D"+sheet.getLastRow()) which should fix the problem.
I am trying to create a data entry form that submits data to a data sheets first open row. The problem is that the data sheet has formula in one of the columns so it is not truly empty. This is causing the current script to take the cells with formula into consideration and only selecting the rows after it.
Could you guys please assist me with a workaround to the issue.
Current script looks like this:
function submitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName("FORM"); //Form Sheet
var datasheet = ss.getSheetByName("DATA"); //Data Sheet
//Input Values
var values = [[formSS.getRange("D4").getValue(),
formSS.getRange("D8").getValue(),
formSS.getRange("D12").getValue(),
formSS.getRange("D16").getValue(),
formSS.getRange("D20").getValue(),
formSS.getRange("D24").getValue(),
formSS.getRange("D28").getValue(),
formSS.getRange("L32").getValue()]];
datasheet.getRange(datasheet.getLastRow()+1, 1, 1, 8).setValues(values);
}
You can find the first free row by evaluating the row contents
Sample
var freeRow;
var columnI = datasheet.getRange("I1:I" + datasheet.getLastRow()).getDisplayValues().flat();
for(var i = 0; i < columnI.length; i++){
if(columnI[i] == "") {
freeRow = i + 1;
break;
}
}
datasheet.getRange(freeRow, 1, 1, 8).setValues(values);
In addition, you are using a Form submit trigger, you use event objects
Sample
function submitData(e) {
var range = e.range;
var row = range.getRow();
// this is the row into which the latest form response has been inserted - do with it what you need
...
}
I'm collecting data from a Google Form that will be used in formulas that calculate costs and mileage. I've used =QUERY to bring the responses to another sheet 'Event Calculator'. When I run the forEach loop to get the data, it picks up cells with a formula.
I would like my code to find the last row with data and pull some numbers from cells to use in an email that will be sent right after they submit the form (on a trigger).
Is there a way to find the last row and then run the forEach loop? or am I going about this all wrong?
I've tried using if statements and forEach, but don't seem to have the correct order and am unable to just find the last row with data in it, not a formula.
The expected results are a single line of data that can be placed in an email function to send the results of the formulas to the respondent.
I've tried using the forEach loop, but it returns rows that contain formulas. I have tried if statements but cannot seem to get it to work with the forEach loop to get the data.
function travelReport() {
// get data from the Sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Event Calculator');
var allRange = sheet.getDataRange();
var allData = allRange.getValues();
// remove the header row
allData.shift();
// loop over rows of data
allData.forEach(function(row) {
// get data
var email = row[0];
var eventName = row[1];
var coordName = row[2];
var startName = row[3];
var destinationName = row[4];
function travelReport() {
// get data from the Sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Event Calculator');
var allRange = sheet.getDataRange();
var allData = allRange.getValues();
// remove the header row
allData.shift();
// loop over rows of data
allData.forEach(function(row) {
The results so far is that the forEach loop picks up all the rows I have a formula in. Several errors occur when I try to add code to find the last row with data.
Try this approach:
function travelReport(e) {
var ss=SpreadsheetApp.getActive();
Logger.log(e);
//I assume timestamp is actually e.values[0]. But you can just look at Logger.log to figure it out.
var email=e.values[1];
var eventName=e.values[2];
var coordName=e.values[3];
var startName=e.values[4]
var destinationName=e.values[5];
GmailApp.sendEmail(email, subject, body, options);//Presumably you know how to configure all of this
}
function createTrigger(name) {
var ss=SpreadsheetApp.getActive();
if(!isTrigger(name)) {
ScriptApp.newTrigger(name).forSpreadsheet(ss.getId()).onFormSubmit().create();
}
}
function isTrigger(funcName){
var r=false;
if(funcName){
var allTriggers=ScriptApp.getProjectTriggers();
for(var i=0;i<allTriggers.length;i++){
if(funcName==allTriggers[i].getHandlerFunction()){
r=true;
break;
}
}
}
return r;
}
Want a more precise answer...Give me more details.
I am unable to get this script to check the whole column and get the last row cell value to compare. It only works if I input a single cell that matches.
Tried this script and have tried many variations I have found on stackoverflow and none work for me.
function CNIC(){
CheckCnic();
}
function CheckCnic() {
// Fetch the Assigned Team
var getassignedto = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Man Ticketing Log");
var range = getassignedto.getRange('N:N');
var lastRow = getassignedto.getLastRow();
var values = range.getValues();
var department = range.getValue();
//var department = lastRow.getValue(); // This is the return value
of CNIC to match
// Check if matches CNIC
if (department == "CNIC"){
// Fetch the email address
var emailAddress = ("myemail#mydomain.com");
// Send Alert Email.
var message = 'ATTENTION ' + department; // Second column
var subject = 'A NEW TICKET HAS BEEN ADDED';
MailApp.sendEmail(emailAddress, subject, message);
}
}
It only gets a single cell because that's exactly what you're asking it to do.
var values = range.getValues();
var department = range.getValue();
getValue() returns a single cell's value. If you want to check multiple rows in a column, either loop through values, or use a TextFinder
I have a spreadsheet on Google Docs with tasks for a job that we are working on at work. What I am wanting it to do is to send the whole row to the intended recipient but I can only get it to send the info from the first column after the email address. Everything has a due date on it and I would like to get it to send a reminder when it gets close to that date but I do not know how to do that.
Any help would be greatly appreciated.
Here is the code I have right now:
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 3; // 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, 2)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var emailAddress = row[0]; // First column
var message = row[1]; // Column B, Column C, Column D, Column E, Column F, Column G
var subject = "Email Test Spreadsheet";
MailApp.sendEmail(emailAddress, subject, message);
}
}
when you define your dataRange like this :
var dataRange = sheet.getRange(startRow, 1, numRows, 2)
you read data from startRow column 1 for numRows but only 2 columns (the last value is 'width') so you could easily modify your script to get all the columns you want just by changing this last value. Then using row[i].toString(), you will get your comma separated fields.
Then you might want to get a better presentation using HTML format to present data in a table for example but this is outside the point of your question ;-)
When you define a data range like this
var dataRange = sheet.getRange(startRow, 1, numRows, 2);
you are taking the data from A3:C5
you can also refer to the documentation
If you can post a sample spreadsheet as per the description of your problem, then it will be helpful for others to help you.
This piece: var message = row[1]; will only ever get the second element (index 1) in the array. You'll need to iterate through the row to get all the array elements.
var message = row[1];
message.concat(row[2]);
message.concat(row[3]);
And so on. Or you could build a loop.
http://www.w3schools.com/jsref/jsref_concat_string.asp
And to get the range, why do it the hard way? sheet.getRange("A2:B3");
That's the range you said you're trying to get in the comment above that line, although it is only 4 cells. sheet.getRange("A2:G3") would get two rows out to column G.
Of course, if you add rows to the sheet, you'd have to modify the code every time.
Instead, try this.
var bottomRow = sheet.getLastRow();
var rangeInA1 = "A2:G"+bottomRow;
var dataRange = sheet.getRange(rangeInA1);