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...
Related
Here is my coding. Help me out.
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").activate();
var lr = ss.getLastRow();
var templateText = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Template").getRange(1,1).getValue();
//for (var i =2;i<=lr;i++){
var currentEmail = column(i,1);
var currentClass = column(i,3).getValues();
var currentName = ss.getRange(i,2).getValues();
var messageBody = templateText.replace("{name}",currentName).replace("{class}",currentClass);
var subjectLine = "Reminder:" + currentClass + " Upcoming Class";
MailApp.sendEmail(currentEmail,subjectLine,messageBody);
Issue:
There are many issues in your code, including undefined variables (e.g. column, i), but I think you want to do the following:
Send an email for each row in Sheet1, taking into account that:
recipient is in column A.
subject is based on the value of column C.
The message body is built by replacing the keywords {name} and {class} from a string template found in Template!A1 with the values of column B and C respectively.
Code snippet:
If the above is correct, you can do the following:
function yourFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Sheet1");
var firstRow = 2;
var numRows = sheet1.getLastRow() - firstRow + 1;
var templateText = ss.getSheetByName("Template").getRange(1,1).getValue();
var values = sheet1.getRange(firstRow,1,numRows,3).getValues(); // Retrieve data from all rows
values.forEach(row => { // Iterate through each row
var [currentEmail, currentName, currentClass] = row; // Get values from columns A-C
var messageBody = templateText.replace("{name}",currentName)
.replace("{class}",currentClass);
var subjectLine = "Reminder:" + currentClass + " Upcoming Class";
MailApp.sendEmail(currentEmail,subjectLine,messageBody);
});
}
Reference:
Tutorial: Sending emails from a Spreadsheet
Best Practices
We have set up a workflow spreadsheet that has job information such as client name, job details etc. I have a nifty script that hides a whole row if you choose "Complete" from a drop down menu in the final column of that row.
What I am trying to do now is send an email with all the data in the row we are hiding.
UPDATED: Here is the code I have so far:
/**
* TITLE:
* Hide a row if a value is inputted then send an email
*/
//**GLOBALS**
// Sheet the data is on.
var SHEET = "Live Jobs";
// The value that will cause the row to hide.
var VALUE = "Complete";
// The column we will be using
var COLUMN_NUMBER = 22
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activeSheet = ss.getActiveSheet();
//Ensure on correct sheet.
if(SHEET == activeSheet.getName()){
var cell = ss.getActiveCell()
var cellValue = cell.getValue();
//Ensure we are looking at the correct column.
if(cell.getColumn() == COLUMN_NUMBER){
//If the cell matched the value we require,hide the row.
if(cellValue == VALUE){
activeSheet.hideRow(cell)
SendEmail(e);
};
};
};
}
/**
* Sends emails with data from the current spreadsheet.
*/
function SendEmail(e) {
// Fetch the email address
var correctSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Live Jobs');
var emailRange = correctSheet.getRange('Z1');
var emailAddress = emailRange.getValue();
var sheet = SpreadsheetApp.getActiveSheet();
var cell = sheet.getActiveCell()
var rowValue = cell.getRow();
var startRow = rowValue; // 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, 1, 5);
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (var i in data) {
var row = data[i];
var message = row; // Second column
var subject = 'Sending emails from a Spreadsheet';
MailApp.sendEmail(emailAddress, subject, message);
}
}
Now, the hide row works, and the email function works if I run it from the Google Script editor, but for some reason, the email does not fire when I select "Complete" but IT WAS WORKING YESTERDAY!!!
Any tips?
M
Try this:
You probably can't do this with a simple trigger since sending email requires authorization.
var SHEET = "Live Jobs";
var VALUE = "Complete";
var COLUMN_NUMBER = 22
function onEdit(e) {
var sh=e.range.getSheet();
if(sh.getName()==SHEET){
if(e.range.columnStart==COLUMN_NUMBER && e.value==VALUE){
sh.hideRow(e.range.rowStart);
SendEmail(e);
}
}
}
}
function SendEmail(e) {
MailApp.sendEmail(e.range.getSheet().getRange('Z1').getValue(), 'Invoice Job Alert', e.range.getSheet(e.range.rowStart,1,1,e.range.getSheet().getLastColumn()).getValues()[0].join(','));
}
I've been searching for the answer to this question but have so far been unable to piece together the answer. Please explain any answer you have in really simple terms as I'm fairly new to GAS and RegEx. I've got most of the syntax down but the execution of it in GAS is giving me a hard time.
Basically, I want to write a script that, when the spreadsheet is edited, checks which rows have yet to be merged. Then, on those rows, creates a copy of a template Google Doc and names the document based on the spreadsheet data. From there (this is the hard part), I need it to replace merge tags in the template with the data from the spreadsheet.
The tags in the templates I'll be using look like this: <<mergeTag>>
My idea was to match the whole tag, and replace it with data from the spreadsheet that exists in the column with the same name as what's inside the "<<>>". Ex: <<FooBar>> would be replaced with the data from the column named FooBar. It would obviously be from the current row that needs the merging.
After that, all that's left is to send an email (a few more row-specific personalization) with that document attached (sometimes as a PDF) with the body of the message coming from an HTML file elsewhere in the project.
This is the whole thing I have so far (notice the placeholders here and there that I can personalize for each spreadsheet I use this for):
function onEdit() {
//SPREADSHEET GLOBAL VARIABLES
var ss = SpreadsheetApp.getActiveSpreadsheet();
//get only the merge sheet
var sheet = ss.getSheetByName("Merge Data");
//get all values for later reference
var range = sheet.getActiveRange();
var values = range.getValues();
var lastRow = range.getLastRow();
var lastColumn = range.getLastColumn();
//get merge checker ranges
var urlColumn = range.getLastColumn();
var checkColumn = (urlColumn - 1);
var checkRow = range.getLastRow();
var checkRange = sheet.getRange(2, checkColumn, checkRow);
var check = checkRange.getBackgrounds();
//get template determination range (unique to each project)
var tempConditionRange = sheet.getRange(row, column);
var tempConditionCheck = tempConditionRange.getValues();
//set color variables for status cell
var red = "#FF0000";
var yellow = "#FFCC00";
var green = "#33CC33";
//////////////////////////////////////////////////////////
//DOC GLOBAL VARIABLES
var docTemplate1 = DriveApp.getFileById(id);
var docTemplate2 = DriveApp.getFileById(id);
var docTemplate3 = DriveApp.getFileById(id);
var folderDestination = DriveApp.getFolderById(id);
//////////////////////////////////////////////////////////
//EMAIL GLOBAL VARIABLES
var emailTag = ss.getRangeByName("Merge Data!EmailTag");
var personalizers = "";
var subject = "" + personalizers;
var emailBody = HtmlService.createHtmlOutputFromFile("Email Template");
//////////////////////////////////////////////////////////
// MERGE CODE
for (i = 0; i < check.length; i++) {
//for rows with data, check if they have already been merged
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, lastRow, (lastColumn - 2))
statusCell.setBackground(red);
//for rows with data, but not yet merged, perform the merge code
//////////////////////////////////////////////////////////
//DOC CREATION
//Determine which template to use
if (tempConditionCheck[i] == "") {
var docToUse = docTemplate1;
}
if (tempConditionCheck[i] == "") {
var docToUse = docTemplate2;
}
if (tempConditionCheck[i] == "") {
var docToUse = docTemplate3;
}
//Create a copy of the template
//Rename the document using data from specific columns, at specific rows
//Move the doc to the correct folder
var docName = "";
var docCopy = docToUse.makeCopy(docName, folderDestination);
var docId = docCopy.getId();
var docURL = docCopy.getUrl();
var docToSend = DriveApp.getFileById(docId);
var docBody = DocumentApp.openById(docId).getBody();
Here's where I need the help
//Locate the Merge Tags
//Match Merge Tags to the column headers of the same name
//Replace the Merge Tags with the data from the matched column, from the correct row
function tagReplace() {
var tagMatch = "/(<{2}(\w+)>{2})/g";
}
statusCell.setBackground(yellow);
urlCell.setValue(docURL);
The rest is just finishing up the process
//////////////////////////////////////////////////////////
//EMAIL CREATION
//Create an email using an HTML template
//Use Merge Tags to personalize email
//Attach the doc we created to the email
//Send email to recipients based on data in the sheet
MailApp.sendEmail(emailTag, subject, emailBody, {
name: "Person McPerson",
attachments: [docToSend], //[docToSend.getAs(MIME.PDF)],
html: emailBody,
});
//////////////////////////////////////////////////////////
//CHECK ROW UPDATE
statusCell.setBackground(green);
}
}
}
My sheets all have a frozen first row that acts as the header row. All my columns will be consistently named the exact same thing as the tags (minus the <<>>).
How do I match the tags to the data?
EDIT
```````````````````
The solution did not work as described when I inserted it into my code as follows:
function formMerge() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Merge Data");
var urlColumn = sheet.getMaxColumns();
var checkColumn = urlColumn - 1;
var lastRow = ss.getSheetByName("Form Responses").getLastRow();
var values = sheet.getDataRange().getValues();
var headers = values[0];
var urlRange = sheet.getRange(2, urlColumn, lastRow);
var checkRange = sheet.getRange(2, checkColumn, lastRow);
var check = checkRange.getBackgrounds();
var red = "#ff0404";
var yellow = "#ffec0a";
var green = "#3bec3b";
var docTemplate = DriveApp.getFileById(id);
var folderDestination = DriveApp.getFolderById(id);
// MERGE CODE
for (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, (urlColumn - 2)).getValues();
var clientNameRange = sheet.getRange((i+2), 3);
var clientName = clientNameRange.getValue();
var dateRange = sheet.getRange((i+2), 2);
var datePreFormat = dateRange.getValue();
var timeZone = CalendarApp.getTimeZone();
var date = Utilities.formatDate(new Date(datePreFormat), timeZone, "MM/dd/yyyy");
statusCell.setBackground(red);
//EMAIL VARIABLES
var personalizers = clientName;
var subject = "Post Intake Report for " + personalizers;
var emailBody = "Please see the attached Google Doc for the Post Intake Report for " + clientName + ". The intake was performed on " + date + ".";
var emailTagRange = sheet.getRange((i+2), 24);
var emailTagValue = emailTagRange.getValue();
var emailTag = emailTagValue.split(", ");
//DOC CREATION
var docToUse = docTemplate;
var docName = "Post Intake Report - " + clientName + " [" + date + "]";
var docCopy = docToUse.makeCopy(docName, folderDestination);
var docId = docCopy.getId();
var docURL = docCopy.getUrl();
var docBody = DocumentApp.openById(docId).getBody().editAsText();
for (var j=0; j<headers.length; j++) {
var re = new RegExp("(<<"+headers[j]+">>)","g");
docBody.replaceText(re, dataRow[j]);
}
statusCell.setBackground(yellow);
urlCell.setValue(docURL);
//EMAIL CREATION
MailApp.sendEmail(emailTag, subject, emailBody, {
name: "Christopher Anderson",
attachments: [docCopy],
html: emailBody
});
statusCell.setBackground(green);
}
}
}
Build the RegExp for each tag on the fly, using the header values from your spreadsheet.
Use Body.replaceText() to perform the replacements.
var values = sheet.getDataRange().getValues();
var headers = values[0];
...
// Loop over all columns. Use header names to search for tags.
for (var col=0; col<headers.length; col++) {
// Build RegExp using column header
var re = new RegExp("(<{2}"+headers[col]+">{2})","g");
// Replace tags with data from this column in dataRow
body.replaceText(re, dataRow[col]);
}
This snippet will operate on a single row; the first couple of declarations should appear outside of your row loop. The column looping is then done after you've created and opened the new document, and obtained the body object.
It loops over all the columns in the spreadsheet, using the header names to find the tags you've defined, and replaces them with the corresponding cell contents for the current row.
I am looking to get a few values from a previously submitted row from a google form response and then send these values, along with the title of the form, along to an end user.
however, I think I may be going about it the wrong way, I can pull up the spreadsheet and sheet and use this to edit a field, but i can not see how to pull the values back out from here, it just comes back as "range". what is the right syntax to get a values from a range?
is there any way to pull the title of the linked form? or the spreadsheet if that is not possible, as that is just "formName" +(responses).
here is the code i have so far, it is probably just missing something simple here (the e value that it accepts is a formatted URL):
function doGet(e){
var params = JSON.stringify(e);
var message = "thank you, your Decision has been sent.";
var id = '10kf5QDeBc-vr6XJnkFxQh-ch_poQhqBQWQbgJ';
var ss = SpreadsheetApp.openById(id);
// var ss = SpreadsheetApp.getActiveSpreadsheet();
// var sheet = ss.getActiveSheet();
var sheet = ss.getSheets()[0];
var column = 20;
var datecolumn = 5;
var row = e.parameter.row;
//var title = sheet.getTitle();
var sheetname = ss.getSheetName();
var sheetnamesheet = sheet.getName();
var eventDaterange = sheet.getRange(row, datecolumn);
var eventDate = eventDaterange.getvalues();
var approval_cell = sheet.getRange(row, column);
//set default to declined
approval_cell.setValue("declined");
var approvedMessage = "<HTML><body>"+
"<h2>your request has been Approved </h2><br />"
+"<P>" + title +" " + sheet
+"<p>" + " for this date: "+ eventDate;
var deniedMessage = "<HTML><body>"+
"<h2>your request has been denied </h2><br />"
+"<P>" + ss +" " + sheet
+"<p>" + " for this date: "+ eventDate;
var aprovalResponce = (e.parameter.approval == 'true') ? approvedMessage : deniedMessage;
//var msg = "Your manager said this: " + aprovalResponce;
var msg = aprovalResponce
var replyEmail = e.parameter.reply;
if (e.parameter.approval == 'true') {
approval_cell.setValue("APPROVED");
} else {
approval_cell.setValue("DECLINED");
}
//send the actual email out
//MailApp.sendEmail(replyEmail, "Approval Request", msg);
MailApp.sendEmail(replyEmail, "Approval Request", {htmlBody: msg});
return HtmlService.createHtmlOutput(message);
}
To get values from a range, you use var values = range.getValues(); This will return a result in an 2D array of values, which is like a grid representing the range, i.e. values[row][column]. So, if our range is, say, "A1:C3", and we want the values in A1 and C3, we could do this:
var range = sheet.getRange("A1:C3");
var values = range.getValues();
var valueOne = values[0][0];
var valueTwo = values[2][2];
var msg = '"Form name" + valueOne + ValueTwo'
I don't know how to get the form name though, perhaps something as simple as e.source.title? If not, if you are using multiple forms, you may have to do this by getting the form Id from the form being submitted, and then using a switch statement to match that ID to a list of form names within your script.
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);
...