onFormSubmit trigger not working in google script - javascript

I wonder if someone can help me.. I've written this code which is supposed to carry out the following workflow:
user fills in the form, form responses are recorded in excel sheet and various calculations take place, final values are appended into a template pdf, and this pdf is sent via email to the user.
The script does do all of that but only when I manually click "run", whereas I want it to execute whenever a form is submitted and I can't understand why it doesn't.
I would add a screenshot of my trigger but I can't as I don't have 10 reputation yet; but my trigger is set up as follows:
Run: onFormSubmit
Events: From spreadsheet, On form submit
I'll paste my code below, does anyone have any ideas as to why it might not be working? Any help would be hugely appreciated.
//Set out global variables
var docTemplate = ("1Ff3SfcXQyGeCe8-Y24l4EUMU7P9TsgREsAYO9W6RE2o");
var docName=("Calculations");
function onFormSubmit(e){
//Variables
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName("Sheet3"));
var totalOutstandingPrincipalDebt = SpreadsheetApp.getActiveSheet().getRange("G25").getValue();
var totalOutstandingInterest = SpreadsheetApp.getActiveSheet().getRange("H25").getValue();
var totalOutstandingCompensation = SpreadsheetApp.getActiveSheet().getRange("I25").getValue();
var dailyInterestRate = SpreadsheetApp.getActiveSheet().getRange("J25").getValue();
var grandTotal = SpreadsheetApp.getActiveSheet().getRange("K25").getValue();
var userEmail = SpreadsheetApp.getActiveSheet().getRange("H24").getValue();
//Template Info
var copyId=DriveApp.getFileById(docTemplate).makeCopy(docName+' for '+userEmail).getId();
var copyDoc = DocumentApp.openById(copyId);
var copyBody = copyDoc.getActiveSection();
//Putting the data into the file
copyBody.insertParagraph(1,'Total Outstanding Principal Debt: £' + totalOutstandingPrincipalDebt);
copyBody.insertParagraph(2,'Total Outstanding Interest: £'+ totalOutstandingInterest );
copyBody.insertParagraph(3,'Total Outstanding Compensation: £'+ totalOutstandingCompensation);
copyBody.insertParagraph(4,'Grand Total: £' + grandTotal);
copyBody.insertParagraph(5,'Daily Interest Rate: £'+ dailyInterestRate);
copyDoc.saveAndClose();
//email pdf document as attachment
var pdf = DriveApp.getFileById(copyId).getAs("application/pdf");
var subject = "Calculations";
var body = "Thank you very much for using our online calculator. Please find your results attached.";
MailApp.sendEmail(userEmail, subject, body, {htmlBody: body, attachments: pdf});
//Deletes temporary Document
DriveApp.getFileById(copyId).setTrashed(true);
}

It looks like instead of using the values attribute of the form submit event you are trying to look directly at the spreadsheet tab that gets values written to it. Think of it this way: you could attach this script to a Form instead of a spreadsheet and have it still work. At the time a form submission happens, the event object e has everything you need.
This is a problem because you have hardwired cell addresses, but the form will keep writing more rows. This means you won't see new rows as they accumulate.
But the big problem is that you're looking at that tab using getActiveSpreadsheet(). When a user submits a form response, there is no "active spreadsheet." The "active" thing was the form. So, in order to get the data that was submitted, you'll need to look in e.values -- this will contain the row of data you're currently trying to get to in lines 9-14.
btw, the reason it works when run manually is because "active spreadsheet" means something. As soon as you walk away, that spreadsheet isn't active.
EDIT:
Zehrazjp20 points out that they are using the spreadsheet for computation, not just reading raw values as from the Form Submit event. In this case, the best way is to replace:
var ss = SpreadsheetApp.getActiveSpreadsheet();`
with
var ssID = 'abcdefghijklmnop';
var ss = SpreadsheetApp.openById(ssID);
...using your spreadsheet's real ID, of course.

Related

Google Sheet script - Automatically send an email with values when they are added to a Google Sheet

I've been trying to follow the document below but have got a bit stuck. In short, I am trying to have a Google Sheet send out an email automatically when two values are added to a Google Sheet (Name, Telephone)
The function below works great when I run it from App script but I am looking for it to grab the values that are inserted at the time and send the email automatically rather than having to run the script each time in App Script.
// Fetch the email address
var emailRange =
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("B2");
var emailAddress = emailRange.getValue();
// Send Alert Email.
var message = 'This is your Alert email!'; // Second column
var subject = 'Your Google Spreadsheet Alert';
MailApp.sendEmail(emailAddress, subject, message);
https://www.groovypost.com/howto/google-sheets-send-email-based-on-cell-value/
You need to use the onEdit feature.
https://developers.google.com/apps-script/guides/triggers/events
function onEdit(e){
sendEmailToUpdatedValue(e)
}
function sendEmailToUpdatedValue(e){
let email_x = /\b[\w\.\-\+]+#[\w\-]+\.[a-zA-Z]{2,13}(\.[a-zA-Z]{2,13}|\b)/;
let email = e.range.getValue();
let is_email = email_x.test(email);
if(is_email){
//your email function here
}
}
Be sure to run the onEdit function once within Apps Script in order to ensure the trigger is set up. That first time will get an error.
Keep in mind that onEdit only fires from a user action, so if the emails are being added from a form or some other script, this will not work. In those scenarios you would need a time based trigger.

How to detect Event Listeners and their actions on input fields

I have purchased a booking plugin (wordpress) to add to a site.
https://wpamelia.com/
I cannot show the site I am working on, but here a demo from plugin developers
https://sports.wpamelia.com/#book
Once you have chosen your date and time, you end up on a form with input fields.
I was able to pre-fill this form with data that I could pass via the URL.
My URL would look something like this: https://sports.wpamelia.com/?first=Jim&last=Tester&email=something%40something.com&phone=0222222222#book
But here is the problem:
Even though I managed to use jQuery to pre-fill the input fields of the form, as soon as I click confirm the fields' content is erased and the error "Please enter... " appears for each of them.
So again:
STEP 1: I open the booking page with an URL containing data in the query string
STEP 2: Using jQuery, I manage to pre-fill the form that appears after having chosen date and time (first name, last name ...)
STEP 3: I click "Confirm"
RESULT: all the fields are empty and for each one the error message "Please enter first name" (etc..) appears
I've messaged the plugin developers. Only answer was that there is indeed no functionality to take the data from the Query String into the form fields yet.
MY QUESTIONS:
1) How could I find out, with chrome inspector or other tools, why exactly the content I pre-fill into the form is ignored?
---> I've tried things like getEventListeners in the chrome inpector's console, but I don't really see how to get information out of that
2) Would anyone know what the issue is and/or how I could bypass it?
---> there is a lot of javascript from the plugin developers behind that and something is expecting manual entering of the data into the fields...
---> but even when trying to fake manual entering with things like $(this).trigger("change").val(function(i,val){return 'aaaa';}); this didn't solve the problem....
(If anyone is interested, I can post later my javascript/jQuery functionality to get the form fields pre-filled with data from Query String... interesting code as you have to wait until the fields appear for jQuery to recognise them..)
Thanks so much for any help!
cheers
Admino
#Admino - this may not be the best solution and I know this is an old question so you may not need it now but after not finding a better one it at least worked for me.
function getUrlVars() {
var vars = {};
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
vars[key] = value;
});
return vars;
}
function valueOutput(element) {
element.dispatchEvent(new Event('input'));
}
jQuery(function() {
jQuery(document).on('change', 'input', function(e) {
valueOutput(e.target);
});
// you may want to perform more validations here if needed
// just checking here if email is present (but not checking for valid email address)
var fname = getUrlVars()["first"];
var lname = getUrlVars()["last"];
var email = getUrlVars()["email"];
var phone = getUrlVars()["phone"];
var custom1 = getUrlVars()["custom1"]; // you know this field label is Order Number
if (email.length > 0) {
// run an interval until the elements are present on the page (form displayed)
var checkInputs = setInterval(function() {
if (jQuery('.amelia-app-booking label[for="customer.email"]').length > 0) {
var em = jQuery('.amelia-app-booking label[for="customer.email"]').closest('.el-form-item').find('.el-input__inner');
// this checks to see if an Amelia customer is already present
if (em.val() == '') {
em.prop('value', email).val(email).trigger('change');
jQuery('.amelia-app-booking label[for="customer.firstName"]').closest('.el-form-item').find('.el-input__inner').prop('value', fname).val(fname).trigger('change');
jQuery('.amelia-app-booking label[for="customer.lastName"]').closest('.el-form-item').find('.el-input__inner').prop('value', lame).val(lame).trigger('change');
jQuery('.amelia-app-booking label[for="customer.phone"]').closest('.el-form-item').find('.el-input-group__prepend').siblings('.el-input__inner').prop('value', phone).val(phone).trigger('change');
}
// for custom fields I check the label text to find the correct input
if (custom1 != '') {
jQuery('.amelia-app-booking label:contains("Order Number")').closest('.el-form-item').find('.el-input__inner').prop('value', custom1).val(custom1).trigger('change');
}
// form info is updated so clear the interval
clearInterval(checkInputs);
}
}, 500);
}
});
You may want to try a different method than url params to sync this info so it's not so public in the url string. This code may not need both the prop and val jquery setters but I just left them for you to try. Hope it helps (and to others I'm open to a better solution)!

Send emails on Google form submission using specific variables

I am really new with this of Google scripts and I need your help.
I have a form that among the fields it has, it has one field named owner this is just a name.
I need to create a script to send a notification to the person that is listed in the owner field when the form is submitted.
I know how to burn an email directly on the script
function myFunction() {
// Fetch the email address
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Test").getRange("B2");
var emailAddress = emailRange.getValues();
// Send Alert Email.
var message = 'This is your Alert email!'; // Second column
var subject = 'Your Google Spreadsheet Alert';
MailApp.sendEmail(emailAddress, subject, message);
}
The problem I am lost with is that I know how to recognize the email depending on the name of the owner which is stored in another sheet.
Here is the link of a sample form and here is the sample spreadsheet
Can anyone share some light?
There are four things for you to understand and research.
1 - Create a script that can be created as an Installable "OnFormSubmit" trigger. Documentation is here.. This will execute every time a form is submitted. This is easy to do, and I have added a screenshot of the add trigger screen at the end of this answer.
2 - Learn about the information captured by an "OnFormSubmit" script. In particular it will return the range of the form submission from which you want the value of Column 7 (the Owner).
3 - The emails sheet contains a separate set of data. You can get it by referencing it with getSheetByName - documentation Ref
4 - You need to look for a match between "Owner and the "Name" value on the "Emails" sheet. There are many options for how to find a match with the owner but looping through the "emails" data is probably the easiest. At each new line you check whether the email name is a match for the "Owner". When you find a match, then you get the accompanying email address (in the cell beside the email name). Then you can send the email as you tested.
This code adapts your existing code to work through the steps mentioned.
function so5524531901(e) {
// this script as an Installable "OnFormSubmit" Trigger
//setup the spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
//get the range from OnFormSubmit
var range = e.range;
//Logger.log("DEBUG: the range is "+range.getA1Notation());//DEBUG
// get the data for the range
var response = range.getValues();
// get the owner name from the form submission
var owner = response[0][7];
Logger.log("DEBUG: Owner = "+owner);// DEBUG
// get the emails list
var emailSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Emails");
// get ALL the data from this sheet
var emaildata = emailSheet.getDataRange().getValues();
// check how many rows of data
var emailLastRow = emailSheet.getLastRow();
// start the loop through the emails data
for (var i=1; i<emailLastRow; i++){
// if owner is equal to Email Name
if (owner == emaildata[i][0]){
// there is a match
//get the email address
var emailAddress = emaildata[i][1];
Logger.log("DEBUG: owner = "+emaildata[i][0]+", email address: "+emailAddress);// DEBUG
// Send Alert Email.
// Uncomment the following rows to declare the message, subject and then send the email.
// var message = 'This is your Alert email!'; // Second column
//var subject = 'Your Google Spreadsheet Alert';
//MailApp.sendEmail(emailAddress, subject, message);
}
}
}

Bad Value, Constant Error

I am a brand new coder, and I want my code to work with this process:
I fill out a form that leads to a spreadsheet
I want that spreadsheet to check certain values in another spreadsheet to see if they match (there are lots of values in that spreadsheet and I want the program to run through the whole column)
If they match, I want to have an email sent to me with the matching person's name, which is displayed in the same row in the spreadsheet I will be comparing to.
My not working code:
function myFunction(e) {
var genderSheet1 = e.values[19];
var genderSheet2 = SpreadsheetApp.openById("To Populate");
if (genderSheet1===genderSheet2) {
var userName = e.values[1];
var userEmail = "email";
var subject = "WORKER FOUND?";
var message = "Dear " + userName + "," +
"\n\n\nThis is the finder" +
MailApp.sendEmail(userEmail, subject, message, {attachments:file.next().getBlob()});
}
}
Please help!
I will go based on a few assumptions
Your code has an alteration in SpreadsheetApp.openById("To Populate") and To Populate is a real ID in the actual code and not "To Populate" as is written here
You have set a trigger to execute the script on Form submit in the script triggers
I also note that you have not mentioned what the problem is so I will have to point out some of the problems in your code as I see it.
First of all var genderSheet1 = e.values[19]; will be a string value, while var genderSheet2 = SpreadsheetApp.openById("To Populate"); is an object, so if (genderSheet1===genderSheet2) will always return a false.
Then we have this bit MailApp.sendEmail(userEmail, subject, message, {attachments:file.next().getBlob()}) however, there is no file defined anywhere in the code. The syntax you are using would indicate that you should have a DriveApp method that returns a fileIterator object. What is the attachment you are trying to send?
Please refer to the form submit trigger event objects in the documentation to see what you should expect when using that event handler.

How do I pass a value from an HTML form submission to a Google Sheet and back to HTML in a Google Apps Script Web App

I'm trying to create a basic time clock web app.
So far, I'm using this script to create this web app which takes the input values and puts them in this spreadsheet for the time stamping part.
I need it to use one of the values from the form and perform a lookup in this sheet (take the longId and find me the name) and return the (name) value to the html page as a verification for the end user that they were identified correctly. Unfortunately, I don't know enough to grasp what I'm doing wrong. Let me know if I need to provide more info.
Edit 1
I'm thinking that I wasn't clear enough. I don't need the user info from entry, I need the user from a lookup. The user will be entering their ID anonymously, I need to match the ID to their info, and bring the info back for them to verify.
Edit 2
Using the link provided by Br. Sayan, I've created this script using this spreadsheet as above to test one piece of this. The web app here spits out: undefined. It should spit out "Student 3" Still not sure what I'm doing wrong.
One way for the next button to grab the student input field:
<input type="submit" onclick="studentName(document.getElementById('student').value)" value="Next..."/>
That sends the value to this func in Javascript.html:
function studentName(value) {
google.script.run
.withSuccessHandler(findSuccess)
.findStudent(value);
}
Which sends it to a findStudent(value) in Code.gs
You do the lookup and the return value goes back to findSuccess( result ) back in Javascript.html. Handle the result from there.
Also consider keeping the stock preventDefault() code that comes with the Web App template in the Help > Welcome Screen.
Please try this one:
(source: technokarak.com)
Also please have a look at:
Retrieve rows from spreadsheet data using GAS
EDIT:
Please make these changes in your function and let us know.
function findValue() {
var data = SpreadsheetApp.openById("15DRZRQ2Hcd7MNnAsu_lnZ6n4kiHeXW_OMPP3squbTLE").getSheetByName("Volatile Data").getDataRange().getValues();
for(i in data) {
if(data[i][3] == 100000003) {
Logger.log("yes");
Logger.log(data[i][0]);
var student = [];
student.push(data[i][0]);
return student;
}
}
}
It is a complicated answer, I have had a lot of success with:
function process(object){
var user = Session.getActiveUser().getEmail();
var key = object.Key;
send(key);
}
function send(k){
var ss =
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lastR = ss.GetLastRow();
ss.GetRange(lastR,1).SetValue(k);
}
On your html button you will need to have inside the tags
onClick="google.script.run
.withSuccessHandler(Success)
.process(this.parentNode);"
In order for this to work, obviously you will need to have your fields named accordingly.
Edit: The only thing I did not include in the code was a Success handler, which will be in your html of the GAS script. This should point you in a direction that can resolve that.
Hope this helps.

Categories

Resources