google apps script trigger event upon update from external source - google-apps-script

I have a Google Sheet that is updated via "Zapier" from my CRM (Capsule) application. I need to auto trigger an email to a given address whenever the spreadsheet is updated. The CRM software successfully adds a new record to the end of the spreadsheet when a new organization is created. I have a script that monitors the last row and sends an email to the addressee by an on change event but this only happens if I go in and change the spreadsheet data myself.
function sendEmail() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var dataRow = sheet.getLastRow();
var emailCell = sheet.getRange(dataRow, 4);
var emailAdd = emailCell.getValues();
var newRecordSource = sheet.getRange(dataRow, 1, 1, sheet.getLastColumn());
var newRecord = newRecordSource.getValues();
//var message = "This record has just been added:" +newRecord;
var message = "New record added to Workflow Sheet!: " +newRecord;
var subject = "Test from Workflow Sheet!";
MailApp.sendEmail(emailAdd, subject, message);
}
Can anyone help?

If the emails will be sent to a Gmail address then you can do this without Apps Script: Log into the account that will receive the emails, open the relevant spreadsheet, and select "Notification rules..." from the Tools menu and set it up as desired.
If you must do it from Apps Script, you will probably need to use a time-based trigger to monitor for changes, which is considerably more complicated than using onChange, unfortunately.

Related

Sheet Duplication Script : Protection not set when other user run the script

I am sharing a Google sheet with others with sharing enabled only with email ids. I have created a script that when run duplicates a master sheet and sets permission and protections as in the master sheet into the duplicated sheet. There is a button placed to execute this script in the master sheet.
When I (Owner) runs the script the protections get copied fine and other users are unable to access the protected cells but the problem arises when other editors of the sheet run the script the new sheet created gives them full access to edit all protected fields also.
The code i have written is as under:
function Protect() {
var spreadsheet = SpreadsheetApp.getActive();
var myValue = SpreadsheetApp.getActiveSheet().getSheetName();
spreadsheet.duplicateActiveSheet();
var totalSheets = countSheets() - 3;
myValue = "DO" + totalSheets;
SpreadsheetApp.getActiveSpreadsheet().renameActiveSheet(myValue);
var protection = spreadsheet.getActiveSheet().protect();
protection.setUnprotectedRanges([spreadsheet.getRange('C2:E5'), spreadsheet.getRange('C6:D6'), spreadsheet.getRange('F5:G5'), spreadsheet.getRange('F6:G6'), spreadsheet.getRange('B9:B18'), spreadsheet.getRange('C9:G18'), spreadsheet.getRange('D20:D22'), spreadsheet.getRange('G20:G22'), spreadsheet.getRange('B20:B22'), spreadsheet.getRange('E21'), spreadsheet.getRange('E20')])
.removeEditors(['user2#domain.com', 'user3#domain.com']);
spreadsheet.getRange('G2').activate();
spreadsheet.getRange('G2').setValue(myValue);
spreadsheet.getRange('G3').activate();
spreadsheet.getRange('G3').setValue(new Date()).setNumberFormat('dd-MMM-YYYY');
spreadsheet.getRange('C2').activate();
hideImage();
};

In Google Apps Scripts, is there a way to create new tabs based on form data, then copy data into that newly created tab from multiple sheets?

I'm setting up a Google Sheet that is connected to a Form. The Form is bringing in data from unique users. When a new user completes the form, and the data is stored in the Form Responses 1 tab, I want a new tab to be created that is named with the name of the new user. Additionally, I want to copy in some data to this newly created tab from two other, separate tabs... one row from the Form Responses 1 tab, and a large range of data and formulas from another tab (right now I have that tab named "Sheet 5").
I'm able to create a new tab and name it based on the unique user that shows up in the Form Responses 1 tab. However, I don't know how to copy the data from those other tabs into the newly created tab because I'm trying to use getSheetbyName, which requires me to provide a named sheet. Well, I don't have a named sheet because the target sheet to be copied into will always be based on new data that comes into the form.
Any help is appreciated. Thanks.
function onFormSubmit(){
Logger.log('submit ran');
var form = FormApp.openById('1oaGxmsd8SEDJ9HrXixpriCeKYrRxr1ZVX0x1zbohTIQ');
ScriptApp.newTrigger('onFormSubmit')
.forForm(form)
.onFormSubmit()
.create();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Form Responses 1');
//Get last row of data
var lastRow = sheet.getLastRow();
var colB_Data = sheet.getRange(lastRow, 2).getValue();
//var thisUser = 'theUserName';
ss.insertSheet(colB_Data);
};
function copyTo() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Sheet5");
var pasteSheet = ss.getSheetByName("John Doe");
var source = copySheet.getRange(1,1,4,2);
var destination = pasteSheet.getRange(pasteSheet.getLastRow()+1,1,4,2);
source.copyTo(destination);
The onFormSubmit function works, but the rest of the code is not working. I can't get data copied into a sheet that doesn't exist yet. It seems like I'd need the "John Doe" to be a variable that copies into each new sheet that is created
Try this:
function onFormSubmit(e){
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet5');
var rg=sh.getRange(1,1,4,2);
var vA=rg.getValues();
var username=e.values[1];
var nsh=ss.insertSheet(username);
nsh.getRange(1,1,vA.length,vA[0].length).setValues(vA);
}
This code is written using the Spreadsheet Script Editor. And you have to create a onformSubmit trigger in the same project for the Spreadsheet.
onFormSubmit Trigger for Spreadsheets

Google Apps Script failing onFormSubmit with concurrent responses

I've setup a Google Form and linked Sheet to record form responses and then create and email the respondent a letter from the submitted data. This has been working fine and has started getting quite popular among colleagues, but now a problem has arisen with onFormSubmit when two users submit the form at the same time.
Essentially, the script is setup to run on the last row of the sheet onFormSubmit but if two responses are submitted concurrently it only runs the script on the last entry, potentially twice.
I'm thinking it might be possible to get around this by setting up a column to be marked if the script completed and then setting up a time-drive trigger to run on any rows that haven't been marked but this feels a bit clunky as respondents usually require the letter immediately.
Is there another way to approach the problem so that onFormSubmit makes sure the script actually runs on the row created by the original form submission and not simply the last row? Any help would be much appreciated, here's an example of my code:
function createLetterFromForm(){
// Get data from sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
// Define range and data in each column
var data = sheet.getRange(sheet.getLastRow(), 1, 1,
sheet.getLastColumn()).getValues(); // Range (last entry submitted)
for (var i in data){
var row = data[i];
// Pick the right template
if (row[9]=="A"){
var templateid = "xxxxxxxxxxxx";} // Template 1
if (row[9]=="B"){
var templateid = "xxxxxxxxxxxy";} // Template 2
// Make copy and set active
var folder = DriveApp.getFolderById("zzzzzzzzzzzzzz") // Folder for generated letters
var docid = DriveApp.getFileById(templateid).makeCopy(row[7]+" - Letter",folder).getId();
var doc = DocumentApp.openById(docid);
var docBody = doc.getActiveSection();
// Copy data to template
// address
docBody.replaceText("%FNAME%", row[2]);
docBody.replaceText("%SNAME%", row[3]);
docBody.replaceText("%ADDL1%", row[4]);
docBody.replaceText("%ADDL2%", row[5]);
docBody.replaceText("%ADDL3%", row[6]);
docBody.replaceText("%PCODE%", row[7]);
// other data
docBody.replaceText("%DATA1%, row[8]);
// etc.
// Share and Save doc
doc.addEditor(row[1]);
doc.saveAndClose();
// Email PDF to Respondee
var sendFile = DriveApp.getFilesByName(row[7]+' - Letter');
var recipient = row[1]
MailApp.sendEmail({
to:recipient,
subject: "Your Letter",
body:"Hello, \n\nHere's a PDF copy of the letter you created.",
attachments: [sendFile.next()]
});
}
}

Delete old data rows in sheet using google forms

I want the sheet that collects data from a google form to delete old data. I have this script in the sheet but it doesn't seem to run when new data is added via the form.
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("1054");
var datarange = sheet.getDataRange();
var lastrow = datarange.getLastRow();
var values = datarange.getValues();// get all data in a 2D array
var currentDate = new Date();
var oneweekago = new Date();
oneweekago.setDate(currentDate.getDate() - 7);
for (i=lastrow;i>=2;i--) {
var tempdate = values[i-1][0];
if(tempdate < oneweekago)
{
sheet.deleteRow(i);
}
The sheet it is linked to is called 1054.
Do I need to put appscript in the Form? If so, what would that be?
I think it would be better to leave the sheet and the form it is attached to alone. Here's what I do on a form that's on my website.
function formSubmitEvent(e)
{
var ss=SpreadsheetApp.openById('SpreadsheetID');
var sht=ss.getSheetByName('ResponseReview');//this is another sheet I used for reviewing the responses.
sht.appendRow(e.values);//I append a row to the ResponseReview sheet
selfEmail(e.values);//and I send myself an email if my quota is above 75
}
Even if you delete old data on the linked sheet the form will continue adding data where it left off because evidently it keeps track of what the next row is.
In general, I leave the linked sheet alone. It is true that I built my form in Googgle Apps Script so at some point when I get a lot of responses then I will unlink the form and delete both and rebuild the form by running that script and a new form and sheet will be linked but I will continue my review process in the other sheet which also has the capability to create a Google Doc from the submitted data. In essence, it doesn't really matter to me where the form wants to put the data because I capture the onFormSubmit trigger and I put into my ResponseReview sheet from the values array. And I can do anything I want with that sheet without affecting the linkage.

Project Trigger Not working

I'm using Cognito forms to collect information and then using Zapier to pass this information to my google spreadsheet database. I have found that my scripts in google spreadsheets do not trigger the way I would expect.
This one:
function onEdit(event){
var ColCR = 96; // Column Number of "CR"
var changedRange = event.source.getActiveRange();
if (changedRange.getColumn() == ColCR) {
var state = changedRange.getValue();
var adjacent = event.source.getActiveSheet().getRange(changedRange.getRow(),ColCR+1);
var adjacentv = adjacent.getValue();
var timestamp = Utilities.formatDate(new Date(), "GMT-7", "M/dd/yy', 'h:mm a");
switch (adjacentv) {
case "":
adjacent.setValue("("+timestamp+")"+" "+state);
changedRange.clearContent();
break;
default:
adjacent.setValue(adjacentv+"\n"+"("+timestamp+")"+" "+state);
changedRange.clearContent();
break;
}
}
}
Works fine when a user edits the spreadsheet cell directly but not when Zapier updates the cell. It also works on several different types of triggers when the spreadsheet is modified by a user. What is the difference between editing the spreadsheet directly vs having an app like Zapier edit the form? Can I write a script that would see an edit by an app like Zapier?
This one:
function Timestamp() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var cell = sheet.getActiveCell();
var comments = cell.getComment();
var formattedDate = Utilities.formatDate(new Date(), "GMT-7", "M/dd/yy', 'h:mm a ");
Newline works in msgBox, but not in Note.
comments = comments + "Mod: " + formattedDate;
Browser.msgBox(comments);
cell.setComment(comments);
}
Works fine when a user edits a cell and is triggered by a On Change trigger and also works when Zapier updates a cell but when Zapier updates more then one cell in a row it only puts the comment in the first cell in the range. How would I modify the script so that it triggers on every change to every cell and not just the range?
Im sure it has something to do with how Zapier is interacting with my spreadsheet but I don't understand how Zapier edits are any different than user edits?
Thanks for any suggestions or recommendations.
I've experienced this issue as well, and in the end came to the conclusion that the Zapier action uses the API to update the Google Sheet, thus not triggering the trigger. Details available here in Google's documentation of Simple Triggers:
https://developers.google.com/apps-script/guides/triggers/