Automating Sheet Creation in newly created Google Sheet+Form - google-apps-script

I am trying to create a script that adds a Google Form response sheet to a newly created Google Sheet, then creates subsequent Sheets in the same Spreadsheet. The sheets are named upon creation. Here is my code thus far:
var statusSheet, form, ss = SpreadsheetApp.openById("SPREADSHEET_ID")
if (ss.getFormUrl() == null){
form = FormApp.create("NEWFORM")
.setCollectEmail(true)
.setProgressBar(false)
.setRequireLogin(true)
.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId())
Utilities.sleep(3000)
}
else{form = FormApp.openByUrl(ss.getFormUrl())}
if (ss.getSheets()[1].getRange("A1").getValue() != 'Status'){
ss.getSheets()[1].setName("Status")
.getRange("A1:C8")
.setValues([["Status",'',"Staff"],["Staff",'',"Student"],["Students",,"building"],["Penalty Box",'',"scope"],["Current Offenders",'',"formCreated"],["Audit Log",'',"formPopulated"],['','',"TimeTrigger"],["Form ID",'',"EditTrigger"]])
Utilities.sleep(3000)
}
statusSheet = ss.getSheetByName("Status")
ss.setActiveSheet(ss.getSheets()[(ss.getSheets().length)-1])
while (ss.getSheets().length < 7){
var newSheet = ss.insertSheet()
var range = statusSheet.getRange(newSheet.getIndex(),2, 1, 1)
range.setValue(newSheet.getSheetId())
newSheet.setName(statusSheet.getRange(newSheet.getIndex(),1, 1, 1).getValue())
Logger.log(newSheet.getName())
}
var staffSheet = ss.getSheetByName("Staff")
var studentsSheet = ss.getSheetByName("Students")
var PenaltyOUSheet = ss.getSheetByName("OrgUnits")
var currentBoxUsersSheet = ss.getSheetByName("Current Users")
var auditLogSheet = ss.getSheetByName("Audit Log")
It seems that all of the sheets are created, except "Staff". This is the case whether the form is created and/or the values are populated either before executing the function or during the execution. To make matters worse, the Logger within the for Loop isn't recording errors.
I have tried encasing the whole function in a try/catch statement, but there are no indications what the error might be other than the reference to staffSheet fails, because no sheets are named "Staff".

Moving the copied range down a row fixed the issue. I'm not positive why it was skipping the Staff sheet rather than the Audit Log, but I will figure that out after I get everything else fixed.

Related

Applying routine refreshes to data with ImportXML & ImportHTML AND monitoring changes [duplicate]

I have a large sheet with around 30 importxml functions that obtain data from a website that updates usually twice a day.
I would like to run the importxml function on a timely basis (every 8 hours) for my Google Spreadsheet to save the data in another sheet. The saving already works, however the updating does not!
I read in Google Spreadsheet row update that it might run every 2 hours, however I do not believe that this is true, because since I added it to my sheet nothing has changed or updated, when the spreadsheet is NOT opened.
How can I "trigger" the importxml function in my Google Spreadsheet in an easy way, as I have a lot of importxml functions in it?
I made a couple of adjustments to Mogsdad's answer:
Fixed the releaseLock() call placement
Updates (or adds) a querystring parameter to the url in the import function (as opposed to storing, removing, waiting 5 seconds, and then restoring all relevant formulas)
Works on a specific sheet in your spreadsheet
Shows time of last update
...
function RefreshImports() {
var lock = LockService.getScriptLock();
if (!lock.tryLock(5000)) return; // Wait up to 5s for previous refresh to end.
var id = "[YOUR SPREADSHEET ID]";
var ss = SpreadsheetApp.openById(id);
var sheet = ss.getSheetByName("[SHEET NAME]");
var dataRange = sheet.getDataRange();
var formulas = dataRange.getFormulas();
var content = "";
var now = new Date();
var time = now.getTime();
var re = /.*[^a-z0-9]import(?:xml|data|feed|html|range)\(.*/gi;
var re2 = /((\?|&)(update=[0-9]*))/gi;
var re3 = /(",)/gi;
for (var row=0; row<formulas.length; row++) {
for (var col=0; col<formulas[0].length; col++) {
content = formulas[row][col];
if (content != "") {
var match = content.search(re);
if (match !== -1 ) {
// import function is used in this cell
var updatedContent = content.toString().replace(re2,"$2update=" + time);
if (updatedContent == content) {
// No querystring exists yet in url
updatedContent = content.toString().replace(re3,"?update=" + time + "$1");
}
// Update url in formula with querystring param
sheet.getRange(row+1, col+1).setFormula(updatedContent);
}
}
}
}
// Done refresh; release the lock.
lock.releaseLock();
// Show last updated time on sheet somewhere
sheet.getRange(7,2).setValue("Rates were last updated at " + now.toLocaleTimeString())
}
The Google Spreadsheet row update question and its answers refer to the "Old Sheets", which had different behaviour than the 2015 version of Google Sheets does. There is no automatic refresh of content with "New Sheets"; changes are only evaluated now in response to edits.
While Sheets no longer provides this capability natively, we can use a script to refresh the "import" formulas (IMPORTXML, IMPORTDATA, IMPORTHTML and IMPORTANGE).
Utility script
For periodic refresh of IMPORT formulas, set this function up as a time-driven trigger.
Caveats:
Import function Formula changes made to the spreadsheet by other scripts or users during the refresh period COULD BE OVERWRITTEN.
Overlapping refreshes might make your spreadsheet unstable. To mitigate that, the utility script uses a ScriptLock. This may conflict with other uses of that lock in your script.
/**
* Go through all sheets in a spreadsheet, identify and remove all spreadsheet
* import functions, then replace them a while later. This causes a "refresh"
* of the "import" functions. For periodic refresh of these formulas, set this
* function up as a time-based trigger.
*
* Caution: Formula changes made to the spreadsheet by other scripts or users
* during the refresh period COULD BE OVERWRITTEN.
*
* From: https://stackoverflow.com/a/33875957/1677912
*/
function RefreshImports() {
var lock = LockService.getScriptLock();
if (!lock.tryLock(5000)) return; // Wait up to 5s for previous refresh to end.
// At this point, we are holding the lock.
var id = "YOUR-SHEET-ID";
var ss = SpreadsheetApp.openById(id);
var sheets = ss.getSheets();
for (var sheetNum=0; sheetNum<sheets.length; sheetNum++) {
var sheet = sheets[sheetNum];
var dataRange = sheet.getDataRange();
var formulas = dataRange.getFormulas();
var tempFormulas = [];
for (var row=0; row<formulas.length; row++) {
for (col=0; col<formulas[0].length; col++) {
// Blank all formulas containing any "import" function
// See https://regex101.com/r/bE7fJ6/2
var re = /.*[^a-z0-9]import(?:xml|data|feed|html|range)\(.*/gi;
if (formulas[row][col].search(re) !== -1 ) {
tempFormulas.push({row:row+1,
col:col+1,
formula:formulas[row][col]});
sheet.getRange(row+1, col+1).setFormula("");
}
}
}
// After a pause, replace the import functions
Utilities.sleep(5000);
for (var i=0; i<tempFormulas.length; i++) {
var cell = tempFormulas[i];
sheet.getRange( cell.row, cell.col ).setFormula(cell.formula)
}
// Done refresh; release the lock.
lock.releaseLock();
}
}
To answer your question for an easy "trigger" to force the function to reload:
add an additional not used parameter to the url you are loading, while referencing a cell for the value of that parameter.
Once you alter the content of that cell, the function reloads.
example:
importxml("http://www.example.com/?noop=" & $A$1,"...")
unfortunately you cannot put a date calculating function into the referenced cell, that throws an error that this is not allowed.
You can also put each XML formula as a comment in the respective cells and record a macro to copy and paste it in the same cell. Later use the Scripts and then the Trigger functionality to schedule this macro.

Trying to reduce run time of GAS code that copies sheets from multiple spreadsheets into a merged master version

OK, so my main question here is if I can reduce the time it takes for this script below to run--it takes on average 8 minutes, 5-6 of which is just from copying sheets into another spreadsheet and pasting their original display values over them.
The purpose of this is to have vendor sheets from four different spreadsheets totaled in alphabetical order for sending as-is or for printing. I already researched trying do use PDFs instead, but have not been able to find a way to be able to print in a specific order with the resources I have--which do not include Adobe premium or access to printers for something like powershell scripts, which I do not know anyway. (Printing a folder of files does not actually print in sequential order, unfortunately, so generating PDFs of each sheet does not give me an option to print in alphabetical order.)
So, my workaround is to get an array of the four source spreadsheets, loop through those with another loop that goes through each sheet, and for each sheet make a copy to the merge spreadsheet, and then get and paste over each sheet with the display values as there would otherwise be issues with formulas not referencing. Then it calls other functions to rename the sheets without "Copy of " on them and then to place them in alphabetical order. For ongoing use, it starts by deleting all the old sheets except for an Index sheet from the merged spreadsheet.
Is there any way to reduce the number of calls? I looked into while loops, but it's different in that a lot of the "data" I'm moving is actual entire sheets, and I need their formatting as well. I considered just inserting the same number of sheets as I would be copying, and then repeating copying the same format which they all have, but that hits into an issue with the names--there can sometimes be duplicate named sheets between the source spreadsheets which have to be allowed in, and copying in just makes additional ones have a number at the end. That is fine, but I cannot set the names of the sheets before they are in the merged spreadsheet due to this, unless I can somehow check for duplicate sheet names and add "1", "2", etc, to them to allow them in.
Is the time running this just inevitable for this process, or can I speed it up?
//https://stackoverflow.com/questions/58834873/copy-google-sheet-values-to-another-google-spreadsheet-and-create-new-sheet-with?rq=1
//https://stackoverflow.com/questions/36758479/google-apps-script-to-iterate-through-a-list-of-spreadsheet-ids-and-copy-range
//Global variables. Runs from the merging spreadsheet, and the IDs of the four source Vendor spreadsheets
var mergeSpreadsheet = SpreadsheetApp.getActive();
var indexSheet = "Index"; //global for alphabetzing script
var monthly732 = '1TLH8HencpyH-4pDkQ1LetTErRxqYjh4gYC0XDdQxSVM'
var monthly827 = '19UfhUAvpFi0UJBF-rQoc4LWcL20-79nRltbSL-Wpj0A'
var quarterly732 = '1BRhoO_GcEoBmV_SoaV2xkw9BjgasAX3CorKxgWuEd2I'
var quarterly827 = '1JaAQtRIiCaQjO_A5S0p-VjHk8LUXExmqOYo75LxWf58'
//main function
function mergeTest2(){
deleteAllSheets(); //calls function at end of script to delete old sheets from merged spreadsheet
//Array of source spreadsheets for looping through
var idList = [monthly732, monthly827, quarterly732, quarterly827];
for (var i = 0; i < idList.length; i++){
//get sheets from each source spreadsheet
var allsheets = SpreadsheetApp.openById(idList[i]).getSheets();
//For each spreadsheet, iterate through sheets except for Index and Template
// Stop iteration execution if the condition is meet.
for(var s in allsheets){
var sheet = allsheets[s];
if(
(sheet.getName() == "Index") ||
(sheet.getName() == "Template") ||
(sheet.getName() == "Totals")
) continue;
//For each sheet, copyTo merged spreadsheet, get original's display values and paste them on the copy
//Using copyTo to get format of sheet, and allow for duplicate sheet names added
var mergeSheet = sheet.copyTo(mergeSpreadsheet);
//Getting display values to override formulas to avoid REF errors
var sValues = sheet.getDataRange().getDisplayValues();
mergeSheet.getRange(1,1,sValues.length,sValues[0].length).setValues(sValues);
} //allsheets, end of for-loop for sheets within a spreadsheet
} //end for-loop of array of spreadsheets
//Get numbers of sheets from each spreadsheet for comparing to make sure all applicable ones were copied
//formulas set in Index page for comparing applicable sheets from sources (Vendors only) to make sure they equal merged Vendor sheets at the end
var index_sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(indexSheet);
index_sheet.getRange("C2").setValue(SpreadsheetApp.openById(monthly732).getNumSheets());
index_sheet.getRange("D2").setValue(SpreadsheetApp.openById(monthly827).getNumSheets());
index_sheet.getRange("E2").setValue(SpreadsheetApp.openById(quarterly732).getNumSheets());
index_sheet.getRange("F2").setValue(SpreadsheetApp.openById(quarterly827).getNumSheets());
index_sheet.getRange("B2").setValue(mergeSpreadsheet.getNumSheets());
//Loggers used for testing, replace with getNumSheets above in final
Logger.log(SpreadsheetApp.openById(monthly732).getNumSheets());
Logger.log(SpreadsheetApp.openById(monthly827).getNumSheets());
Logger.log(SpreadsheetApp.openById(quarterly732).getNumSheets());
Logger.log(SpreadsheetApp.openById(quarterly827).getNumSheets());
Logger.log(mergeSpreadsheet.getNumSheets());
slicerOfNames(); //call slicer function below for removing "Copy of " from sheet names.
sortSheetsByName(); //call function to put sheets in alphabetical order
} //end mergeTest2 main function
//Function to get sheet names except for Index, or that all have "Copy of " in name, and slice(8)
function slicerOfNames(){
mergeSpreadsheet.getSheets().forEach(function(sheet) {
var sheetName = sheet.getSheetName();
if (sheetName.indexOf("Copy of ") == -1) {
Logger.log(sheetName);
} else {
sheet.setName(sheetName.slice(8));
} //end if/else
}) //end function(sheet)
} //end slicer function
//Function for alphabetical ordering of sheets within merged spreadsheet
function sortSheetsByName() {
var aSheets = new Array();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var allsheets = ss.getSheets();
for (var s in allsheets)
{
var ss = SpreadsheetApp.getActive();
var sheet = allsheets[s];
if(
(sheet.getName() == indexSheet)
) continue;
aSheets.push(ss.getSheets()[s].getName());
}
if(aSheets.length)
{
ss.getSheetByName(indexSheet).activate()
ss.moveActiveSheet(1)
aSheets.sort();
for (var i = 0; i < aSheets.length; i++)
{
var theSheet = ss.getSheetByName(aSheets[i]);
if(theSheet.getIndex() != i + 2){
ss.setActiveSheet(theSheet);
ss.moveActiveSheet(i + 2);
} //end if statement
} //end for-loop
} // end if(aSheets.length)
}//end alphabetization function
//Function to delete old sheets from merged spreadsheet at the beginning of the script
function deleteAllSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var result = SpreadsheetApp.getUi().alert("This will delete all sheets except the Index, are you sure?", SpreadsheetApp.getUi().ButtonSet.OK_CANCEL);
if(result === SpreadsheetApp.getUi().Button.OK) {
var sheets = ss.getSheets();
for (i = 0; i < sheets.length; i++) {
switch(sheets[i].getSheetName()) {
case "Index":
break;
default:
ss.deleteSheet(sheets[i]);}}
} else {
SpreadsheetApp.getActive().toast("Sheets not deleted");
}
} //end delete sheets function
In your code, it looks you set values of disjoint ranges five separate times, which is very expensive. You could combine these into one line like combined_range.setValues(2D_array). If you are worried about overwriting any existing data, use combined_range.getValues(), and then manipulate the returned 2D array using Apps Script prior to setting.
Following a similar philosophy, logging can be made much more efficient as well; in general, consider implementing a hash map to store and organize the results of frequent spreadsheet calls. Alternatively, variables can also otherwise be used to condense, hasten, and improve overall readability.

onFormSubmit() not working in Google Form?

[UPDATE]
I had a look at add-ons and I am afraid this won't work. So let me take a step back and describe what I am trying to achieve.
I have a spreadsheet A, with a list of individual events. Each event is a line item in the spreadsheet. The spreadsheet is very long for one, and has many fields that I don't need to expose to event owners (different events different owners). Which means if I allow all these different people edit access to the sheet, it becomes really chaotic.
The solution I came up with is to generate unique IDs programmatically for each event, which I've done. Then for each event, I create an individual form and a pre-filled link, with pre-filled answers that is pulled from the cell values. I intend to give the pre-filled links to event owners when they need to make any updates.
The issue is now I have 100+ forms, and I don't want to have 100+ corresponding tabs set as destinations of these forms. These 100+ forms need to submit responses to one same sheet (tab). Instead I wrote a function for submitted responses to find the right event (the event unique ID is the title of the form) and updates the right cell. This is what you see below processSubmission().
I have tried to write the processSubmission() in the spreadsheet where the events are listed. If I don't set this spreadsheet as destination of these 100+ forms then the spreadsheet doesn't know there is a "submission" event. Therefore the setting the trigger onFormSubmit() in the spreadsheet doesn't work.
Then I moved onFormSubmit() -> processSubmission() and it doesn't set the trigger because as you all pointed out, it's an installable trigger.
What I did manage to to write an onOpen() -> create the onFormSubmission() trigger. That means I had to manually open 100 forms and close them to create that trigger. The triggers are created alright. But turned out for the trigger to actually run I need to manually grant permission!
When I looked at add-on triggers, it says "Add-ons can only create triggers for the file in which the add-on is used. That is, an add-on that is used in Google Doc A cannot create a trigger to monitor when Google Doc B is opened." So I think that also rules out the add-on triggers. So now I am out of ideas.
[ORIGINAL]
I made a custom function for the processing of submission responses. I use the form title as a key, and the response answers are written to the corresponding headers in the row with the right key.
My first try was something like this. But it simply didn't execute when the form was submitted:
function onFormSubmit(e){
var form = FormApp.getActiveForm();
var key = form.getTitle();
var responses = e.response;
var ss= SpreadsheetApp.openById(ss_id);
var sheet = spreadsheet.getSheetByName('Launch list');
var frozenRow = sheet.getFrozenRows();
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getLastColumn();
var headers = sheet.getRange(1, 1, 1, lastColumn).getValues()[0];
var keyCol = headers.indexOf(key_header) + 1;
var header1Col = headers.indexOf(header_1) + 1;
var header2Col = headers.indexOf(header_2) + 1;
var header3Col = headers.indexOf(header_3) + 1;
var keysRange = sheet.getRange(frozenRow+1, keyCol , lastRow - frozenRow, 1);
var allKys = keysRange.getValues();
for (i=0; i<allKys.length; i++){
var keyValue = allKys[i][0];
if (keyValue === key){
var rowNum = l + frozenRow + 1;
break;
}
else {
continue;
}
}
var dataRow = sheet.getRange(rowNum, 1, 1, lastColumn).getValues()[0];
var lookUp = {};
lookUp[item_title_1] = header1Col ;
lookUp[item_title_2] = header2Col ;
lookUp[item_title_3] = header3Col ;
var items = form.getItems();
var cnt = 0;
var changes = [];
for (i=0; i< items.length; i++){
var item = items[i];
var title = item.getTitle();
var itemResponse = responses.getResponseForItem(item);
var existingValue = dataRow[lookUp[title] -1];
if ((itemResponse.getResponse() !=='' || itemResponse.getResponse() !== undefined) && itemResponse.getResponse() != existingValue){
cnt++;
var cell = sheet.getRange(rowNum, lookUp[title], 1, 1);
cell.setValue(itemResponse.getResponse());
changes.push(title);
}
else {
continue;
}
}
Logger.log('Made ',cnt,'changes for launch ',featureID,': ',changes);
}
I also tried a slightly different approach but also didn't work:
function onFormSubmit(){
processSubmission();
}
// Processing form submission
function processSubmission() {
var form = FormApp.getActiveForm();
var key = form.getTitle();
var responses = form.getResponses()[form.getResponses().length-1];
// The rest is the same.
}
Manually running the function in the second approach proved my function processSubmission() works. Manually add a onFormSubmit() trigger via the Apps Script Dashboard is not going to be possible because I am generating hundreds of forms (one for each key) programmatically so I chose to have onFormSubmit(e) in the template and every new form is a copy of the template which should also have copies of these functions. But it just doesn't work! Any insight?
The onFormSubmit trigger is an installable trigger which means that it requires to be set up before being able to use it.
It's also important to keep in mind the following, according to the installable triggers documentation:
Script executions and API requests do not cause triggers to run. For example, calling FormResponse.submit() to submit a new form response does not cause the form's submit trigger to run.
What you can do instead is to create the trigger programmatically, something similar to this:
function createTrigger() {
ScriptApp.newTrigger('onFormSubmit')
.forForm('FORM_KEY')
.onFormSubmit()
.create();
}
Reference
Apps Script Installable Triggers;
Apps Script FormTriggerBuilder Class.

How to get access new sheet, created by adding a new form to spreadsheet?

Im quite new to this thing and never had to post a question before.
Here is the situation :
I want to copy the same form over and over to the same spreadsheet('memoSs'). (original form being 'formFileSample').
I am trying to access the sheet linked to that newly added form. (form added using .setDestination()
Unfortunately that sheet is missing from the array returned by '.getSheets'!
Even though it appears in the spreadsheet 'memoSs' (checked by opening it)
I hope it is understandable with the script below.
any chance somebody might have a way in doing so?
function updateOfMemoSs() {
var memoId = 'xxxxxxxxxxxxxxxxxxxxxxxxx'
var Interface = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Interface');
var memoSs = SpreadsheetApp.openById(memoId);
var formFileSample = DriveApp.getFileById(memoSs.getSheetByName('Sample (Source)').getFormUrl().match(/[-\w]{25,}/));
var sheetsToCopy = setInstrList(); //sets a list of names for the sheets to be created
//looks like [['xxx'],['xx'],['x']]
for(var i in sheetsToCopy){
var newFormId = formFileSample.makeCopy('memo '+sheetsToCopy[i][0]).getId();
var newForm = FormApp.openById(newFormId).setDestination(FormApp.DestinationType.SPREADSHEET, memoId);
// var memoSs = SpreadsheetApp.openById(memoId); //didnt work
var sheets = memoSs.getSheets().filter(sheet => sheet.getFormUrl()); //sets a list of linked sheets
Logger.log('formIds = ');
for(var j in sheets){
Logger.log(sheets[j].getName());
Logger.log(sheets[j].getFormUrl().match(/[-\w]{25,}/));
}//returns a list that does not include the linked sheet created earlier.(even though it appears in spreadsheet)
/* for(var j in sheets){
var sheet = sheets[j];
if(sheet.getFormUrl().match(/[-\w]{25,}/) == newFormId){
var newSheet = sheet; //therefore I never find a match for newFormId
}
}*/
}
// Logger.log('newSheet = ');
// Logger.log(newSheet.getName());
}
Thanks Cooper for considering the question.
It seems i haven't been patient enough in my research as the answer had already been posted in StackOverFlow : 'getSheets() Google Apps Script multiple calls issue'
Somehow after creating a sheet.The following should be called before calling getSheets() :
SpreadsheetApp.flush()

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.