I don't work with Google Sheets very often and mainly deal with Google Form's script editor. We have created a database from various forms that fill into one spreadsheet and I would like to send email notifications to specific managers based on the recent row update, which would update from a form submission. Here is what I was trying to get it to do:
if cell in Column P matches to "FALSE":
send email
set the recipient field as "director1#gmail.com" if cell in Column J matches to the word "Building 1"
set the CC field as "manager1#gmail.com" if the cell in Column I matches to the word "Department 1"
if Column P matches to "TRUE", then it doesn't have to do anything
function triggerOnEdit(e)
{
alertManagers(e);
}
function checkClearance(e)
{
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getSheetByName("Calculations");
var value = sheet.getRange("P:P").getValue();
var range = sheet.getRange(1, 1, sheet.getLastRow(), 1); //understands spreadsheet
if(range.getColumn() <= 16 &&
range.getLastColumn() >=16 )
{
var edited_row = range.getRow();
var approval = SpreadsheetApp.getActiveSheet().getRange(edited_row,16).getValue();
if(approval == 'FALSE')
{
return edited_row;
}
}
return 0;
}
function alertManagers(e)
{
var clearance_row = checkClearance(e);
if(clearance_row <= 0)
{
return;
}
sendEmailByRow(clearance_row);
}
function sendEmailByRow(row)
{
var values = SpreadsheetApp.getActiveSheet().getRange(row,1,row,16).getValues();
var row_values = values[0];
var mail = composeAlertEmail(row_values);
var manageremail='test#gmail.com';
//Uncomment this line for testing
//SpreadsheetApp.getUi().alert(" subject is "+mail.subject+"\n message "+mail.message);
MailApp.sendEmail(manageremail,mail.subject,mail.message);
}
function composeAlertEmail(row_values)
{
var name = row_values[6];
var email = "test#gmail.com";
var message = "Good day, \n The following employee does not have clearance to enter the building: "+name+
" email "+email;
var subject = "Employee Not Cleared: "+name+" "
return({message:message,subject:subject});
}
Does this seem possible? I know I am probably far off from a solution, but thanks if anyone can help in advance!
Solution:
Here is what you are looking for:
function triggerOnEdit(e) {
sendEmails(e)
}
function sendEmails(e){
const row = e.range.getRow();
const col = e.range.getColumn();
const as = e.source.getActiveSheet();
const subject = "This is the subject of the email";
const body = "This is the body of the email";
const to = "director1#gmail.com";
const cc = "manager1#gmail.com";
if(as.getName() == "Calculations" && col == 16 && as.getRange(row,col).getDisplayValue() == "FALSE") {
if (as.getRange(row,10).getValue()=="Building 1"){
if(as.getRange(row,9).getValue()=="Department 1"){
MailApp.sendEmail({to:to ,subject: subject,body:body,cc:cc});
}
else {
MailApp.sendEmail({to: to,subject: subject,body:body});
}
}
}
}
I assume that if column J does not match Building 1 which means there is no recipient, the email won't be sent.
Don't forget to add triggerOnEdit(e) to the current project's trigger:
Related
My google sheets document has multiple sheets (tabs). How do I specify the spreadsheet in which code should work?
I am using the last code from the post below to collect the date and user who changes a particular cell, but the code works for all spreadsheets (tabs) in my document.
Is It Possible To Write Username Of Editor To Cell In Google Docs Spreadsheet
I would like it to work on just one spreadsheet: "Test"
I tried this but to no avail:
var s = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Test");
function getOwnName(){
var email = Session.getActiveUser().getEmail();
//Browser.msgBox("getOwnName");
//var self = ContactsApp.getContact(email);
var self = false;
// If user has themselves in their contacts, return their name
if (self) {
// Prefer given name, if that's available
var name = self.getGivenName();
// But we will settle for the full name
if (!name)
{
name = self.getFullName();
}
//Browser.msgBox(name);
return name;
}
// If they don't have themselves in Contacts, return the bald userName.
else {
var userName = Session.getActiveUser().getEmail();
//Browser.msgBox(userName);
return userName;
}
}
function onEdit() {
var s = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Test");
var r = s.getActiveCell();
if( r.getColumn() == 14 ) //checks the column
{
var nextCell = r.offset(0, 1);
nextCell.setValue(new Date());
//Browser.msgBox("hey!");
//s.setActiveSelection(p + "15");
//show();
//SpecialonOpen();
var nCell = r.offset(0,2);
//Browser.msgBox("olah:" + getOwnName());
nCell.setValue(getOwnName());
}
}
The active cell is the one that is being edited, no matter what the sheet is.
If you want to stick to getActiveCell(), you need to implement an if statement verifying if you are in the right sheet:
function onEdit() {
if(SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName()=="Test")
{
var s = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var r = s.getActiveCell();
if( r.getColumn() == 14 ) //checks the column
{
...
A different elegant solution would be to work with e.range:
function onEdit(e) {
if(SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName()=="Test")
{
var r = e.range;
if( r.getColumn() == 14 ) //checks the column
{
...
I have an onEdit function where if executed should send an email. However, some research has turned up that the mailApp functions don't work with onEdit. I'm currently toying around with workarounds but wondering if others have solutions they've come up with.
function onEdit(e) {
var sheet = e.source.getActiveSheet();
var sheetName = sheet.getName();
//if editing specific sheet
if (sheetName == "draft_test" ) {
//if editing 6th column
var r = e.source.getActiveRange();
if (r.getColumn() == 6 ) {
var player = sheet.getActiveCell().getValue();
// Display a dialog box with a message and "Yes" and "No" buttons.
var ui = SpreadsheetApp.getUi();
var response = ui.alert('Do you want to draft '+player+'?', ui.ButtonSet.YES_NO);
// Process the user's response.
if (response == ui.Button.YES) {
//***********FOR SOME REASON EXECUTING THIS FUNCTION ISNT WORKING******************
emailLeague();
//sortAutoRoster();
} else {
}
}
}
}
function emailLeague() {
var draftSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("draft_test");
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("test_email");
var emailSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Manager Info");
//actual email address omitted
var recipient = 'emailaddress#gmail.com';
//get array of all picks on "draft test" tab, and find latest one
var picks = draftSheet.getRange(3,6,146,1).getValues();
for (var i=0; i<picks.length; i++){
if (picks[i] == ""){
var latestPick = i;
break;
}
}
var subject = sheet.getRange(i+1, 2).getValue();
var body = sheet.getRange(i+1, 1).getValue();
MailApp.sendEmail(recipient, subject, body);
}
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
}
}
My spreadsheet is composed of a main sheet that is populated using a form plus several other sheets for the people who work with the responses submitted through the form. A script delegates the form responses to these other sheets depending on the type of item described in the response.
The problem is, when Person A deletes an item from their respective sheet, it doesn't delete in the main sheet.
My idea is that when you type a set password into the corresponding cell in row 'Q' in Person A's sheet, it matches the item by timestamp to the original form submission and deletes both the version of the item in Person A's sheet as well as the main sheet. However, I can't figure out what to set the range to to get it to point to the row in the array. Everything I have tried has sent back "undefined" in the debugger and won't delete anything. I think the problem is that I don't know how to get the row from the array that I have made. See my code below:
function onEdit() {//copies edited items from individual selector sheets back onto main spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var actSheet = ss.getActiveSheet();
var responseSheet = ss.getSheetByName("Item Request");
var actCell = actSheet.getActiveCell();
var actRow = actCell.getRow();
var actVal = actCell.getValue();
var actLoc = actCell.getA1Notation();
var last = actSheet.getLastRow();
var respLast = responseSheet.getLastRow();
var dataA = responseSheet.getRange(1, 1, respLast, 1).getValues(); //compiles an array of data found in column A through last row in response sheet
var tstamp1 = actSheet.getRange(actCell.getRow(), 1);
var tsVal1 = tstamp1.getValue();
var colEdit = actCell.getColumn();
//===========THIS IS WHERE I'M STUCK=======================
if ((actVal == "p#ssword") && (colEdit == 17)) {
for (i = 1; i < dataA.length; i++) {
if (dataA[i][0].toString == tsVal1.toString()) {
responseSheet.deleteRow(i + 1);
actSheet.deleteRow(actRow);
break;
}
}
}
else if (colEdit == 15) { //checks the array to see if the edit was made to the "O" column
for (i = 1; i < dataA.length; i++) {//checking for timestamp match and copies entry
if (dataA[i][0].toString() == tsVal1.toString()) {
var toEdit = responseSheet.getRange(i + 1, 16);
toEdit.setValue(actVal);
}
}
}
else if (colEdit == 16) { // checks the array to see if the edit was made in the "P" column
for (i = 1; i < dataA.length; i++) {//checking for timestamp match and copies entry
if (dataA[i][0].toString() == tsVal1.toString()) {
var toEdit = responseSheet.getRange(i + 1, 17);
toEdit.setValue(actVal);
}
}
}
else {return;}
}//end onEdit
I don't believe these are proper commands delRow.deleteRow();actCell.deleteRow(); Take a look at the documentation;
Okay I rewrote that function for you a bit but I'm stilling wondering about a couple of lines.
function onEdit(e)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var actSheet = ss.getActiveSheet();
var responseSheet = ss.getSheetByName("Item Request");
var actCell = actSheet.getActiveCell();
var actRow = actCell.getRow();
var actVal = actCell.getValue();
var colEdit = actCell.getColumn();
var respLast = responseSheet.getLastRow();
var dataA = responseSheet.getRange(1, 1, respLast, 1).getValues();
var tstamp1 = actSheet.getRange(actRow, 1);
var tsVal1 = tstamp1.getValue();
for(var i=0;i<dataA.length;i++)
{
if(new Date(dataA[i][0]).valueOf()==new Date(tsVal1).valueOf())
{
if (actVal=="p#ssword" && colEdit==17)
{
responseSheet.deleteRow(i + 1);
actSheet.deleteRow(actRow);
}
else if(colEdit==15)
{
var toEdit = responseSheet.getRange(i + 1, 16);//?
toEdit.setValue(actVal);//?
}
else if (colEdit == 16)
{
var toEdit = responseSheet.getRange(i + 1, 17);//?
toEdit.setValue(actVal);//?
}
}
}
}
Can you explain the function of the lines with question marked comments?
I am trying to send auto generated mail based on text value of Google sheet.
In My Sheet column, F is the row with the status " Approved' Or "Rejected"
If Status is " Approved' mail should generate
I have created the code, however, i think need small modifications
function myNotification() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var ss_sheet = ss.getSheetByName('Form Responses 1')
var ss_sheet_datarange = ss.getDataRange().getValues();
var ss_sheet_lastrow = ss_sheet.getLastRow()
for (var i = 5; i < ss_sheet_lastrow; i++)
var approvalstatus = ss_sheet_datarange[5][i] // column F is the row with the status in
var approval_status = 5;
if (approval_status == 'Approved') {
var email = Session.getUser().getEmail();
MailApp.sendEmail(email, "Test", "Test");
}
Change
if (approval_status == 'Approved') {
For
if (approvalstatus == 'Approved') {
You have two variables very similar, but in your code, approval_status will ALWAYS be 5