Why does updating a form cause onFormSubmit trigger to run? - google-apps-script

My code here:
function script1(){
//What is the form and data item ID?
var form = FormApp.openById("Form ID");
var namesList = form.getItemById("Item ID").asListItem();
//What sheet is used to update the form?
var ss = SpreadsheetApp.getActive();
var names = ss.getSheetByName("Update");
// Grab the values in the sixth column of the sheet - use 2 to skip header row
var namesValues = names.getRange(2, 6, names.getMaxRows() - 1).getValues();
var departmentNames = [];
// Convert the array ignoring empty cells
for(var i = 0; i < namesValues.length; i++)
if(namesValues[i][0] != "")
departmentNames[i] = namesValues[i][0];
// Populate the drop-down
namesList.setChoiceValues(departmentNames);
}
appears to triggers the form to submit causing problems. I have an onFormSubmit trigger set up to copy the data to a database and then delete the row. Unfortunately, the headers keep getting copied to the database since the form does not actually send any data and the only row with data is the headers. How can I update the form without the form submitting? Screenshot example is included below.
[
I hope I was able to make myself clear. Thank you for any help.
Modified Screenshot
Here is a screenshot of the trigger and its executions.

Related

Google Appscript: Copy Data from a Form Sheet to Another Sheet in Last Row and Clear Form

Trying to get an Appscript to run but I can't seem to figure out the last few steps.
Background: I have a Google Sheet with two tabs, #1 is called "Data Form" which hosts a fillable form to capture Transaction information to then be input onto tab #2 called "Posted Transactions". This is a personal budget spreadsheet..
Anyways, The script below is intended to take the information input on the "Data Form", verify what the last row with data is on the "Posted Transactions" tab based on whether or not Column A has any data. (To further clarify, the "Posted Transactions" tab has formulas in columns G-I which prohibits me from using a simple "Find last Row" script.)
As it is written now, I receive an error "Exception: The number of columns in the data does not match the number of columns in the range. The data has 6 but the range has 1.
RecordNewTransaction # Code.gs:24"
Any suggestions to make this work properly?
UPDATE:
var dataRange = datasheet.getRange(lastRow+1,1,1,datasheet.getLastColumn()-3);
After many attempts at trial and error, I needed to edit the line shown above. Current & full script is performing as expected now and is shown below. Image snips of what I was trying to accomplish are also shown for reference.
function RecordNewTransaction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName("Data Form"); //Form Sheet
var datasheet = ss.getSheetByName("Posted Transactions"); //Data
//Input Values
var values = [[formSS.getRange("B4").getValue(),
formSS.getRange("B6").getValue(),
formSS.getRange("B8").getValue(),
formSS.getRange("B10").getValue(),
formSS.getRange("B12").getValue(),
formSS.getRange("B14").getValue()]];
var columnToCheck = datasheet.getRange("A:A").getValues();
var lastRow = getLastRowSpecial(columnToCheck);
Logger.log(lastRow);
var dataRange = datasheet.getRange(lastRow+1,1,1,datasheet.getLastColumn()-3);
Logger.log(dataRange);
var dataValues = dataRange.getValues();
Logger.log(dataValues);
dataRange.setValues(values);
Logger.log(dataRange.setValues(values))
formSS.getRange('B4:B14').clearContent();
};
function getLastRowSpecial(range){
var rowNum = 0;
var blank = false;
for(var row = 0; row < range.length; row++){
if(range[row][0] === "" && !blank){
rowNum = row;
blank = true;
}else if(range[row][0] !== ""){
blank = false;
};
};
return rowNum;
};
Try
var dataRange = datasheet.getRange(lastRow,1,values.length,values[0].length)
replace 1 as necessary (this is the firts column where the data will be stored
Reference:
getRange(row, column, numRows, numColumns)
See edited question above which contains the revised script needed.
The problem here was that columns G-I of my results sheet contain formulas (shown in grey) which required this line below to be modified to grab the last column -3, otherwise the script was looking at too many columns that it didn't need to. Also had to modify "lastRow" to "lastRow+1" because this kept overriding the very last line of data I already had input and the +1 will make new data go to the next available row.
var dataRange = datasheet.getRange(lastRow+1,1,1,datasheet.getLastColumn()-3);

Get the last updated CELL in Google sheets and then grab the row. GAS

So I have a google form feeding into a sheet, and from the spreadsheet I'm making google docs from a template with the answers in the spreadsheet.
The form has the ability to save mid way and come back later via a custom question asking if they want to save and comeback (this submits the form). The script on my spreadsheet activates onFormSubmit so when they quit, the template gets created with half their answers. When they eventually come back and finish it off, I want the script to know where to create the template from.
For instance, 5 more rows were added since they quit and the script creates the template from a manual change of the line 'var tactics' by changing the numbers to the row. e.g. if I was about to test another entry, I'd change the numbers to the next empty row first, then when the form is submitted, it would use that row. Not practical. It wouldn't work.
I've looked around a bit and found onEdit(e) but that doesn't work unless it's a manual entry.
My question is, is there another way other than onEdit to find the last cell UPDATED, not ADDED else it'll grab the last row in the sheet, which I don't want. If it grabs the last cell updated then it will grab the correct row to run the script for. I'll add my script to the bottom if it'll help. ID's etc. have obviously been removed.
Any ideas?
function onFormSubmit(e) {
var Sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
var headers = Sheets.Spreadsheets.Values.get('myID', 'A1:U1');
var tactics = Sheets.Spreadsheets.Values.get('myID', 'A6:U6');
var templateID = "myID"
for(var i = 0; i < tactics.values.length; i++){
var Timestamp = tactics.values[i][0];
var IDCFREF = tactics.values[i][2];
var raisedby = tactics.values[i][4];
var AccMan = tactics.values[i][6];
var Contrib = tactics.values[i][7];
var Contract = tactics.values[i][8];
var CompName = tactics.values[i][9];
var ValidFrom = tactics.values[i][10];
var ValidTo = tactics.values[i][11];
var Freq = tactics.values[i][12];
var PDetailFreq = tactics.values[i][13];
var BillType = tactics.values[i][14];
var TypeOfRebate = tactics.values[i][15];
var RebateDetails = tactics.values[i][16];
var RTarget = tactics.values[i][17];
var GiveDeets = tactics.values[i][19];
var WhyGiveRebate = tactics.values[i][20];
var documentID = DriveApp.getFileById(templateID).makeCopy().getId();
DriveApp.getFileById(documentID).setName('Rebate ' + IDCFREF + ' Request');
var body = DocumentApp.openById(documentID).getBody();
var header = DocumentApp.openById(documentID).getHeader();
header.replaceText('##IDCF##', IDCFREF)
body.replaceText('##REF##', IDCFREF)
body.replaceText('##RAISED##', raisedby)
body.replaceText('##ACCMAN##', AccMan)
body.replaceText('##CONTRIB##', Contrib)
body.replaceText('##SIGNED##', Contract)
body.replaceText('##NAME##', CompName)
body.replaceText('##FROM##', ValidFrom)
body.replaceText('##TO##', ValidTo)
body.replaceText('##FREQ##', Freq)
body.replaceText('##BESPOKE##', PDetailFreq)
body.replaceText('##BILL##', BillType)
body.replaceText('##TYPE##', TypeOfRebate)
body.replaceText('##DEETS##', RebateDetails)
body.replaceText('##TARGET##', RTarget)
body.replaceText('##FULL##', GiveDeets)
body.replaceText('##ELAB##', WhyGiveRebate)
}
}
So for anyone who has the same issue, here was my solution:
function onFormSubmit(e) {
var range = e.range;
var ss = range.getSheet();
var row = range.getRowIndex();
var tactics = Sheets.Spreadsheets.Values.get('ID', "A:AQ"+row);
The way it works is just delete every row in the form responses sheet up until the last entry, and then voila. ("A:AQ"+row) gets the row index from the variable 'row' created from 'e.range' upon form submission.
It works great for me, if 3 forms were filled in and 3 lines were added to the sheet before another came back to edit his responses, the script knows what row to find the data on in the sheet, thus creating the correct template.

Using insertText to edit a google form response on google sheet

I have a form that stores responses in a google sheet and what I'm trying to do is on form submit, I want to add or insert a text to a column to turn the response to a hyperlink. For example the response is 123456, I need to convert it to https://www.google.com/123456. I currently have this script but I'm getting an error on the insertText part.
var hyperlink = 'https://www.google.com/'
function onformsubmit(e){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Form Responses 1');
var rg = sheet.getRange(1,3,sheet.getLastRow(),1);
var data = rg.getValues();
for(var i=0;i<data.length;i++){
var datstr=String(data[i][0]).insertText(0,hyperlink);
data[i][0]= (datstr);
}
rg.setValues(data);
}
Any suggestion will be much appreciated.
For the insert part I think what you want is this code in the for-loop:
for(var i=0; i < data.length; i++){
data[i][0] = hyperlink + data[i][0];
}
rg.setValues(data);
That will change all text in that column to include the modified hyperlink. If you want to just modify the new row whenever you submit a form response you'll need to access the values or range from the event argument (e), using e.range, e.values, e.namedValues or probably some combination. See the documentation here.

Populating a Google From DropDown with Values from a Google Sheet

I looked through all of the questions, and I can't quite figure out what I am doing wrong. Here is the background:
I created a spreadsheet that has multiple tabs. Each tab corresponds to types of events (e.g parades, festivals, sports). Each of these tabs is used to assign specific job numbers to a specific event. Parade job numbers are in the 2000 range, festivals 3000 range, sports 4000 range.
I then have a different tab which defines an array that collects all of the job numbers and events, and sorts them alphabetically. This tab is called "Active_Job_Numbers".
My goal is to create several forms that call to this spreadsheet for the values on the "Active_Job_Numbers" tab. I sort of have this working, but not completely. Each form has a purpose such as submitting a request to accounting to process a credit card transaction; another form to submit a request to cut a check.
All of these requests have to be tied to an ever changing list of job numbers. When I update the spreadsheet, the dropdown in the spreadsheet will populate based on the "Active_Job_Numbers" data.
The code that I am using does work, BUT if I update a job number or event name on the spreadsheet, the form still sees the old value. I have triggers set up for onSubmit and onOpen and still, the only way I can get the dropdown in the form to update is if I change one of the "var" labels. Then save it. Then reopen the form. This is a showstopper.
Here is the code I am using:
function updateForm(){
// call your form and connect to the drop-down item
var creditCardRequestGoogleForm = FormApp.openById("1LqtPLGEhocGdkg75mkdldfplxSxWJmYZCp3u1bMwVQQ");
var jobNumberDropDown = creditCardRequestGoogleForm.getItemById("1632374512").asListItem();
// identify the sheet where the data resides needed to populate the drop-down
var ss = SpreadsheetApp.openById("17fgel9Jfl4Aske85mfkwsphBQpZgtGhR8Q");
SpreadsheetApp.setActiveSpreadsheet(ss);
var activeJobNumbers = ss.getSheetByName('Active_Job_Numbers');
// grab the values in the first column of the sheet - use 2 to skip header row
var jobNumValues = activeJobNumbers.getRange(2, 1, activeJobNumbers.getMaxRows() - 1).getValues();
var jobNums = [];
// convert the array ignoring empty cells
for(var i = 0; i < jobNumValues.length; i++)
if(jobNumValues[i][0] != "")
jobNums[i] = jobNumValues[i][0];
// populate the drop-down with the array data
jobNumberDropDown.setChoiceValues(jobNums);
}
A separate but related problem...there are some forms I have that have two different drop-down elements that call to the same spreadsheet but to different tabs. I am not sure how to group this script so I have to separated out like so:
function updateForm(){
// call your form and connect to the drop-down item
var ccrForm = FormApp.openById("1r5n6Sr7fhtGCqht_QHy9qWR1v2ESabxdE-bMxjR-M");
var jobNumberDropDown = ccrForm.getItemById("1678955392").asListItem();
// identify the sheet where the data resides needed to populate the drop-down
var ss = SpreadsheetApp.openById("17eGAlcnBB7ejdfqp3HBQBK9wJAKWYhBQpZgtGhR8Q");
SpreadsheetApp.setActiveSpreadsheet(ss);
var currentJobNumbers = ss.getSheetByName('Active Job Numbers');
// grab the values in the first column of the sheet - use 2 to skip header row
var jobNumValues = currentJobNumbers.getRange(2, 1, currentJobNumbers.getMaxRows() - 1).getValues();
var jobNums = [];
// convert the array ignoring empty cells
for(var i = 0; i < jobNumValues.length; i++)
if(jobNumValues[i][0] != "")
jobNums[i] = jobNumValues[i][0];
// populate the drop-down with the array data
jobNumberDropDown.setChoiceValues(jobNums);
}
function updateForm(){
// This call your form and connect to the drop-down item
var form = FormApp.openById("1r5n6Sr7fhtGCqht_QHy9qWR1v2ESabxdE-bMxjR-M");
var poNumberDropDown = form.getItemById("57865789991").asListItem();
// This is the second dropdown which calls the same spreadsheet but different tab NEED HELP COMBINING THESE TWO MORE ELEGANTLY
var ss = SpreadsheetApp.openById("17eGAlcnBB7ejdfqp3HBQBK9wJAKWYhBQpZgtGhR8Q");
SpreadsheetApp.setActiveSpreadsheet(ss);
var currentPONumbers = ss.getSheetByName('PO_Number');
// grab the values in the first column of the sheet - use 2 to skip header row
var poNumValues = currentPONumbers.getRange(2, 1, currentPONumbers.getMaxRows() - 1).getValues();
var poNums = [];
// convert the array ignoring empty cells
for(var i = 0; i < poNumValues.length; i++)
if(poNumValues[i][0] != "")
poNums[i] = poNumValues[i][0];
// populate the drop-down with the array data
poNumberDropDown.setChoiceValues(poNums);
}
Any insight would be much appreciated.
onOpen trigger for a form, as mentioned in documentation:
This event does not occur when a user opens a form to respond, but
rather when an editor opens the form to modify it
Instead, you can use an installable trigger to activate your code when the sheet is edited.
Furthermore, instead of modifying all form each time an edit is made on the spreadsheet. You can check for which sheet is modified in the spreadsheet to modify the corresponding form. To determine the which sheet is modified you can use event objects.
Here is example code, you can modify this for your use.
function installedEdit(evtObj){
Logger.log("Edit running")
var sheetName = evtObj.range.getSheet().getSheetName()
Logger.log(sheetName)
var itemId="None"
switch (sheetName){
case "Active Job Numbers":
itemId = "1678955392"
updateForm(sheetName,itemId)
break;
case "PO_Number":
itemId = "57865789991"
updateForm(sheetName,itemId)
break;
}
}
function updateForm(sheetName,itemId){
// call your form and connect to the drop-down item
var form = FormApp.openById("1r5n6Sr7fhtGCqht_QHy9qWR1v2ESabxdE-bMxjR-M");
var dropDown = form.getItemById(itemId).asListItem();
// identify the sheet where the data resides needed to populate the drop-down
var ss = SpreadsheetApp.openById("17eGAlcnBB7ejdfqp3HBQBK9wJAKWYhBQpZgtGhR8Q");
SpreadsheetApp.setActiveSpreadsheet(ss);
var sheet = ss.getSheetByName(sheetName);
// grab the values in the first column of the sheet - use 2 to skip header row
var choiceValues = sheet.getRange(2, 1, sheet.getLastRow() - 2).getValues();
var choices = [];
// convert the array ignoring empty cells
for(var i = 0; i < choiceValues.length; i++)
if(choiceValues[i][0] != "")
choices[i] = choiceValues[i][0];
// populate the drop-down with the array data
dropDown.setChoiceValues(choices);
}
Note: Make sure installedEdit() is set up to run when the spreadsheet is edited and is bound to the spreadsheet.

Querying a Column in a Google Script

I've setup a script to copy and process a spreadsheet that is generated from a form.
I gather the responses like this:
var responses = formResponses.getDataRange().getValues();
Then accessing the individual columns through the array index, for example:
var timestamp = row[0];
var agentName = row[1];
Now, while this works. Any changes to the form could end up screwing the whole thing up and the array indices will change.
Can I instead query by column name?
If accessing it via the onFormSubmit event is not viable for you because you need to access the data post-submission, I have created code that allows you to access via column name.
function getDataFields(workingRow){
//header is row 1 -- not row 0
var headerRow = 1;
var headerRowValues = getRowRange(headerRow).getValues();
var workingRowValues = getRowRange(workingRow).getValues();
var dataFields = [];
for (var colNum in headerRowValues[0]){
dataFields[headerRowValues[0][colNum]] = workingRowValues[0][colNum];
}
dataFields['Working Row'] = workingRow;
return dataFields;
}
function getRowRange(workingRow){
var activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lastColumn = activeSheet.getLastColumn();
return activeSheet.getRange(workingRow, 1, 1, lastColumn);
}
You can then use dataFields['Column Name'] to access the data of that particular row.
if you use the onFormSubmit event you can access all form values by their named values, this is explained in the documentation on Spreadsheet Form Submit Events so you won't have this problem anymore...