Fixing a trigger - google-apps-script

I need a trigger (or two) and I'm doing something wrong. Or maybe I need to tweak my code.
I want an email to send automatically when I update a line in my spreadsheet. The three columns that update are notes, status, and resolution. Right now, in my test version, it sends an email as soon as I change any one of those columns. Which is fine, if I'm only updating one thing. But if I want to add a note, change the status, and enter a resolution all at once, it sends three separate emails. My first trigger for sending an email upon form submission works great.
Here is my code. Any help would be appreciated
PS: I'm sure there are cleaner ways to do this, but I'm familiar with this from the past. It's just been long enough that I don't remember all of it.
function formSubmitReply(e) {
var userEmail = e.values[3];
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
// Set the status of the new ticket to 'New'.
// Column F is the Status column
sheet.getRange("F" + lastRow).setValue("New");
// Calculate how many other 'New' tickets are ahead of this one
var numNew = 0;
for (var i = 2; i < lastRow; i++) {
if (sheet.getRange("F" + i).getValue() == "New") {
numNew++;
}
}
MailApp.sendEmail(userEmail,
"Helpdesk Ticket #" + lastRow,
"Thanks for submitting your issue. \n\nWe'll start " +
"working on it as soon as possible. You are currently " +
"number " +
(numNew + 1) + " in the queue. \n\nHelp Desk.",
{name:"Help Desk"});
}​
function emailStatusUpdates() {
var sheet = SpreadsheetApp.getActiveSheet();
var row = sheet.getActiveRange().getRowIndex();
var userEmail = sheet.getRange("D" + row).getValue();
var subject = "Helpdesk Ticket #" + row;
var body = "We've updated the status of your ticket.\n\nStatus: " + sheet.getRange("F" +
row).getValue();
body += "\n\nNotes: " + sheet.getRange("E" + row).getValue();
body += "\n\nResolution: " + sheet.getRange("G" + row).getValue();
MailApp.sendEmail(userEmail, subject, body, {name:"Help Desk"});
}
function onOpen() {
var subMenus = [{name:"Send Status Email", functionName: "emailStatusUpdates"},
{name:"Schedule Appointment", functionName: "scheduleAppointment"},
{name:"Push to KB", functionName: "pushToKb"}];
SpreadsheetApp.getActiveSpreadsheet().addMenu("Help Desk Menu", subMenus);
}​

It looks that you are using an on edit trigger to send and email that works OK when you only need to edit one cell but when you need to edit two or more the email should be sent until you ended to edit all the related cells.
One way to achieve the desired behaviour is to add one or more conditions to send the email.
One way to implement the above is to add an auxiliary column to set if the email should be sent inmediately or not. What about calling this "Hold" and add a checkbox?
This is just is a brief example of how to implement the above as an installe on edit trigger
/**
* Columns C,D,E are notes, status and resolution respectively
* Column F is Hold
*/
function sendUpdate(e){
if(e.range.columnStart > 2 && e.range.columnStart < 6 || e.range.columnStart === 6){
var hold = e.range.getSheet().getRange(e.range.rowStart, 6).getValue();
if( hold !== 'TRUE'){
// Call here your send email function
} else {
// Ask if the email should be sent now
}
}
}

Related

Google Sheets Send SMS to a specific person based on status change of a cell

If you are reading my question, Thank you for taking the time out of your day to help.
Background:
I have a form that my field techs use in order to request parts, I would like to keep them updated on the status of a part order with an automated sms text to their phone.
Specifics:
Link to test Sheet
https://docs.google.com/spreadsheets/d/1hEDEk-3-z3Wh6PNLoY6PgmbSJZ7OcdMR0kFCl0BRrYU/edit?usp=sharing
Parts Request (Picture)
Status Change
When a Status is changed in (column G), this will trigger an SMS to be sent.
Recipient
The SMS will be sent to the Team Lead ( Column B) in that row.
Example: Status is changed (G2), SMS is sent to Team Lead (B2).
Employee Info (Picture)
Employee Information:
The Script pulls the Employee Telephone number(Employee Info! B2) from the Employee Info Sheet
Text Body:
The Message that is sent would be the entire row in the text message
-Team Lead
-Type of Request
-Job Name
-Part Description
-QTY Missing
-Status
The Script i have tried so far has been a simple one, based on a trigger of anytime a change is made to the sheet. Here is what i have used so far, this has worked and has been sending generic texts. Any Help would be greatly appreciated.
function sendText() {
var EmailTo = "'Mobile Number'"#txt.att.net";
var subject = "Whatever";
var body = "Text";
MailApp.sendEmail(EmailTo, subject, body);
}
The processes needed to achieve your goal involves fetching the desired details on the row wherein the status of the request changes, incorporating the given phone number to the appropriate carrier domain, and adding an installable trigger so that the script will automatically work when there are changes to the Status Column.
Here is the script:
function sendUpdates(e) {
var ss = e.source;
var shEmployeeInfo = ss.getSheetByName("Parts Request");
var shPartsRequest = ss.getSheetByName("Employee Info");
var row = e.range.getRow();
var column = e.range.getColumn();
if(column == 7) { //To limit checking of changes to the Status column
var info = shEmployeeInfo.getRange(row, 2, 1, 6).getValues();
var header = shEmployeeInfo.getRange(1, 2, 1, 6).getValues();
var carrier = shPartsRequest.getRange(row,4).getValues();
const subject = "Insert Subject Here."; //Edit this to change the subject
const carrierMap = { "ATT": 'txt.att.net',
"T-Mobile": 'tmomail.net',
"Sprint": 'messaging.sprintpcs.com',
"Verizon": 'vtext.com',
"Cricket": 'mms.mycricket.com' }
var phoneNumber = shPartsRequest.getRange(row,2).getValues();
var emailTo = `${phoneNumber}#${carrierMap[carrier]}`;
var body = "";
for(let i = 0; i <= 5; i++) {
body += header[0][i] + ": " + info[0][i] + "\n";
}
MailApp.sendEmail(emailTo, subject, body);
}
}
The installable trigger should be set as:
Please refer to the Installable Triggers Guide for more information.
As for the result, I made a test case and got this:

How to assign a tag to an event that would allow for 3d conflict detection on a google calendar?

I'm attempting to create a resource management system that would allow members of my office to reserve a room through forms, creating a calendar event on a public calendar.
The issue I am facing in the creation process revolves around conflict checking complications, due to the fact that I would like to have all the calendar events located on one calendar per office. The result is multiple resources on one calendar, and the need for my conflict checking script to not only check for conflicting events but also evaluate the room in which the events are.
Ideally, I would need the script to check first if there are any conflicts, and if there are, it would then check the room name of the conflict event. If the events are in different rooms, it will approve the event creation. If otherwise, it would be denied, and the form respondent would receive an email. I've scoured through the documentation, but with my limited knowledge of the language and coding in general I haven't been able to find a good solution.
At first, I thought I would be able to compare event descriptions, which I would set as the room name based on input from the form submissions. When I called all of the conflicts within the start and end time I would then evaluate their descsriptions with the description of the event to be created and check from there. Unfortunately, from what I tested, it is not possible to do with multiple conflicting events.
I then have what I have listed here where I have an array of the rooms submitted in the past, from the spreadsheet attached to the form, and it checks through them. The issue here is that it doesn't account for start and end times only room names.
My principal question in all of this would be, is there a way to assign a tag of some sort to my event on creation that I would then be able to call back in the conflict checking portion of my script. Ideally, this tag would be something I can assign myself and then evaluate with the new form submission data. I think the best way would be through inviting a room resource to the event, but I have been unable to figure out how to do that as well.
function getConflicts(request){
var conflicts = request.calendar.getEvents(request.dateTime, request.edateTime);
//var description = conflicts.getDescription();
if(conflicts.length >= 1){
Logger.log(conflicts.length);
for (var i=0; i<conflicts.length; i++) {
if (request.room != request.slroom[i]) {
request.status = "Approve";
} else if (request.room == request.slroom[i]) {
request.status = "Conflict";
}
}}
else request.status = "Approve";
}
Full Script:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getLastColumn();
var calendarb = CalendarApp.getCalendarById("link");
function Submission(){
var row = lastRow;
var slrow = (lastRow-1);
this.timestamp = sheet.getRange(row, 1).getValue();
this.name = sheet.getRange(row , 3).getValue() + ": " + sheet.getRange(row, 5).getValue();
this.email = sheet.getRange(row, 2).getValue();
this.dateTime = sheet.getRange(row, 6).getValue();
this.edateTime = sheet.getRange(row,7).getValue();
this.room = sheet.getRange(row, 5).getValue();
this.slroom = sheet.getRange(2, 5, (lastRow-1)).getValues();
this.roomstart =sheet.getRange(2, 6, (lastRow-1)).getValues();
this.roomend = sheet.getRange(2, 7, (lastRow-1)).getValues();
//this.roomInt = this.room.replace(/\D+/g, '');
//this.status;
this.calendar = calendarb;
return this;
var event;
}
function getConflicts(request){
var conflicts = request.calendar.getEvents(request.dateTime, request.edateTime);
//var description = conflicts.getDescription();
if(conflicts.length >= 1){
Logger.log(conflicts.length);
for (var i=0; i<conflicts.length; i++) {
if (request.room != request.slroom[i]) {
request.status = "Approve";
} else if (request.room == request.slroom[i]) {
request.status = "Conflict";
}
}}
else request.status = "Approve";
}
function draftEmail(request){
request.buttonLink = "link";
request.buttonText = "New Request";
switch (request.status) {
case "Approve":
request.subject = "Confirmation: " + request.room + " Reservation for " + request.dateTime + "-" + request.edateTime;
request.header = "Confirmation";
request.message = "Your room reservation has been scheduled.";
break;
case "Conflict":
request.subject = "Conflict with " + request.room + " Reservation for " + request.dateTime + "-" + request.edateTime;
request.header = "Conflict";
request.message = "There is a scheduling conflict. Please pick another room or time."
request.buttonText = "Reschedule";
break;
}
}
function updateCalendar(request){
var event = {
summary: request.name,
location: 'Location',
description: request.room,
start: {
dateTime: request.dateTime.toISOString()
},
end: {
dateTime: request.edateTime.toISOString()
},
attendees: [
{email: request.email}
],
tag: request.room
};
event = Calendar.Events.insert(event, "Calandar ID");
// request.calendar.addSmsReminder(10);
Logger.log('Event Tag: ' + event.description);
Logger.log('Event ID: ' + event.id);
}
function sendEmail(request){
MailApp.sendEmail({
to: request.email,
subject: request.header,
htmlBody: makeEmail(request)
})
sheet.getRange(lastRow, 8).setValue("Sent: " + request.status);
}
function main(){
var request = new Submission();
getConflicts(request);
draftEmail(request);
if (request.status == "Approve") updateCalendar(request);
sendEmail(request);
}
My spreadsheet is set up in this order
A: Timestamp
B: Email Adress
C: Name
D: Location
E: Room
F: Start Date & Time
G: End Date & Time
Sample Sheet
Based on your code, you store your room details in your event's description. Why not use the descriptions of the conflicted events to check if the room is still available? In this manner, we don't need to tag our events.
I also modified some lines as to improve the efficiency of your whole script. Kindly see changes below.
Code:
function Submission() {
// get values by bulk
var rowValues = sheet.getRange(lastRow, 1, 1, 7).getValues().flat();
// avoid using this as that contains another values you don't need
// create a request variable instead
var request = {};
request.timestamp = rowValues[0];
request.name = rowValues[2] + ": " + rowValues[4];
request.email = rowValues[1];
request.dateTime = rowValues[5];
request.edateTime = rowValues[6];
request.room = rowValues[4];
request.calendar = calendarb;
// No need to store previous lists of rooms and their time.
// See getConflicts for clarification
return request;
}
function getConflicts(request) {
var conflicts = request.calendar.getEvents(request.dateTime, request.edateTime);
// Since you stored the room into the description
// Filter the conflicts by comparing conflicts' descriptions to the room
if(conflicts.filter(conflict => conflict.getDescription() == request.room).length > 0)
request.status = "Conflict";
else
request.status = "Approve";
}
Changes:
Get the row values by bulk.
Create request variable instead of using this. The latter contains other values you don't need.
Removed getting past room and date time values for other events.
Makes use of the descriptions of the conflicts and filter them by comparing that to your room.

Custom Email based on Cell values in google sheets

I need help while building a Request Approval Flow in google Forms/Sheets
I have a data response sheet similar to like below, Column A to J headers are
"Timestamp" "EmailAddress" "Name" "Targets" "Account#" "Reason" "Access(Days)" "Approver" "Approved" "CaseID"
I have already setup a form submit email trigger to Approval body through formMule AddOn, Now I want to trigger email when Approval body approve or disapprove the request in Data response sheet.
Everytime when anyone select "Y" or "N" to column "I", script should suppose to trigger an email based on the data present in that row.
I am not an expert but tried to do it with following script and unfortunately not getting desired outcome, I set it up Current project trigger to OnEdit
function sendNotification1(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Form Responses 1");
if(e.range.getColumn() == 9 && e.value == "Y")
{
var name = e.range.offset(0,-6).getValue();
var comment = e.range.offset(0,-3).getValue();
var email = e.range.offset(0,-7).getValue();
var case1 = e.range.offset(0,1).getValue();
var approver1 = e.range.offset(0,-1).getValue();
var subject = "Request has been Approved";
var body = "Hi " + name + ", your Change Request number " + case1 + " has been approved by " + approver1 + " with following comments: " + "\n\r" + comment;
MailApp.sendEmail(email, subject, body);
}
if(e.range.getColumn() == 9 && e.value == "N")
{
var name = e.range.offset(0,-6).getValue();
var comment = e.range.offset(0,-3).getValue();
var email = e.range.offset(0,-7).getValue();
var case1 = e.range.offset(0,1).getValue();
var approver1 = e.range.offset(0,-1).getValue();
var subject = "Request has been Disapproved";
var body = "Hi " + name + ", your Change Request number " + case1 + " has been Dis-Approved by " + approver1 + " with following comments: " + "\n\r" + comment;
MailApp.sendEmail(email, subject, body);
}
}
StackDriver logging showed the following error, but couldn't identify where the issue exist while referring the cell addresses.
2018-09-14 08:19:06.336 PKT
The coordinates or dimensions of the range are invalid. at sendNotification1(Code:6)
I am able to trigger an email on any edit event with following code, but no luck with conditional edit event trigger.
function sendNotification1(e) {
MailApp.sendEmail("myemail#companygmail.com", "Sample subject", "Sample body");
}
While debugging the code, I can see the following error
TypeError: Cannot read property "range" from undefined. (line 6, file "Code")
Any help will be highly appreciated
You are likely running into the issue of the code not knowing what range you are referring to, because you never tell it where to look.
Add the following to the top of the function:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("YOUR SHEET NAME");
And it should work perfectly.
EDIT: Here's the email I sent to myself when I tested it.

Google app script - how to count number of google form response

It is my first time writing google app script and I am desperately need help.
The purpose is to set up a workshop sign up form. Based on how many people already signed up, an email is sent out to inform if sign up was successful, or was put in the wait list.
I copied code from a tutorial. But need help to get the count of form responses. Here is how it looks like now:
function onFormSubmit(e) {
var timestamp = e.values[0];
var yourName = e.values[1];
var toAddress = e.values[2];
var subject = "Workshop Confirmation";
var emailBody = "Thank you for your submitted on " + timestamp
var num_response = COUNT_NUMBER_OF_RESPONSE // <-- need help here
var LIMIT = 15
if (num_response <= LIMIT) {
emailBody = emailBody + "\n\nYou are enrolled in the workshop";
}
else {
var wait = num_response - LIMIT
emailBody = emailBody + "\n\nThe workshop is full. You are #" + wait + " in the waiting list"
}
emailBody = emailBody + "\n\nThe details you entered were as follows: " +
"\nYour Name: " + yourName +
"\nYour Email: " + toAddress ;
MailApp.sendEmail(toAddress, subject,
emailBody, optAdvancedArgs);
}
I have no clue how to find right answer in the google app document. Any help would be greatly appreciated!
How about the composite function
FormApp.getActiveForm().getResponses().length
no need to go around looking for the spreadsheet (since in my experience, the spreadsheet is not always up to date, whereas the form is)
From what I see in the tutorial, this script is embedded in a spreadsheet so the easiest would be to count the number of rows and substract 1 because of the headers...
There is a method for that : getLastRow(), the doc refered in this link should give you enough information to write the few lines of code you need...
test :
function xx(){
var lr = SpreadsheetApp.getActiveSheet().getLastRow()-1;
Logger.log(lr);
}
Script on form (not spreadsheet)
function onFormSubmit(e) {
var num_response = FormApp.getActiveForm().getResponses().length
var LIMIT = 20 //or whatever
if (num_response < LIMIT) {
}
else {
var form = FormApp.getActiveForm();
form.setAcceptingResponses(false);
}
}

Set Value on a DIFFERENT Sheet in Google Apps Script

I want to do a variation on the "Sending emails from a Spreadsheet" tutorial on the Google Developers page.
I want to do exactly what they walk me through EXCEPT that when it comes to marking the cell in each row every time the e-mail is sent, I want to mark that on a SEPARATE sheet.
So, on the sheet "NeedReminders", my script finds the e-mail addresses of members to whom the e-mail should be sent in column A (which then becomes variable "emailAddress"). It sends the e-mail to that address.
THEN--and here's where I need help--I want the script to go to the "Master" sheet, find the row where emailAddress is in column X, and--in that same row--set the value of column BT.
Shouldn't be difficult. I just don't necessarily know how to do the "find the row where columm X equals emailAddress and set value of BT to today's date" part.
Who can help? Thanks in advance!
Here's my code so far (I'm always embarrassed to show my code because I'm sure it's terrible juvenile, so be gentle, please!):
function sendDuesReminder() {
var sheet = SpreadsheetApp.openById('___').getSheetByName('NeedReminders');
var startRow = 4; // There are a bunch of headers and other nonsense.
var valueSheet = SpreadsheetApp.openById('___').getSheetByName('Master');
// Determine how many rows to process
var rowRange = sheet.getRange(3,4); // On my sheet, cell D3 counts how many members are on the list of people who need a reminder e-mail.
var rowData = rowRange.getValue();
// End Determine how many rows to process
// Send e-mail
var dataRange = sheet.getRange(startRow, 1, rowData, 2);
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
// var emailAddress = row[0]; //Column A: E-mail Address
var name = row[1]; //Column B: First Name
var subject = "Reminder About Dues for Newcomers & Neighbors";
MailApp.sendEmail("matt#twodoebs.com", subject, "Hi, " + name + ". It's been some time since we received your online registration for Newcomers & Neighbors. But we don't seem to have received your dues payment, yet. So we wanted to drop you a quick line and remind you about it." + "\n\n" + "If you think we should have received your payment by now or there's some kind of a mistake, please let us know by responding to this message." + "\n\n" + "Otherwise, please send a check for __ made payable to ___ to:" + "\n\n" + "___" + "\n" + "___" + "\n" + "___" + "\n" + "___" + "\n\n" + "Thanks! Please don't hesitate to reach out if you have any questions." + "\n" + "___" + "\n" + "___" + "\n\n" + "Best wishes," + "\n\n" + "Kati" + "\n" + "Membership Director",
{name:"___"});
// End Send E-Mail
// Set that an a-(Experimental)
var valueRange = valueSheet.getRange // Here's where I kind of break down . . . I need *something* here that searches the sheet for emailAddress
setValue(today()) //I'm also not sure that this is the right way to set the value as today's date
SpreadsheetApp.flush();
// End Set value
}
Browser.msgBox("OK. Reminder messages have been sent. doebtown rocks the house!")
}
If you're still struggling, try this:
var emails = valueSheet.getRange('X:X').getValues();
var targetRange = valueSheet.getRange('BT:BT');
var dates = targetRange.getValues();
for (var j=0; j<emails.length; ++j) {
if (emails[j][0] == emailAddress) {
dates[j][0] = new Date();
break;
}
}
targetRange.setValues(dates);