Running a similar App Scripts code on Multiple Sheets - google-apps-script

Just started to use App Scripts recently (Beginner programmer here).
I'm trying to automate the creation of a column + transposition of data from column B to newly created column C.
It's working fine, but when I try to replicate the script on multiple sheets, only one of the script works.
Below is my code (I tried to duplicate my constants to replicate the first script but then the first script does not work anymore. I guess I'm doing it the wrong way) :
// First script working fine.
const ss_1 = SpreadsheetApp.getActiveSpreadsheet();
const sheet_1 = ss_1.getSheetByName('Marketing Report');
function insertColumn() {
const range_1 = sheet_1.getRange('B1:B300').getValues();
const newrange_1 = sheet_1.getRange('C1:C300');
const tmp_1 = new Date();
const yesterday_1 = new Date(tmp_1.setDate(tmp_1.getDate()-1));
sheet_1.insertColumnAfter(2);
newrange_1.setValues(range_1);
sheet_1.getRange('B2').setValue(yesterday_1).setNumberFormat("MMMM");
}
// Second script, I tried to duplicate my constants to replicate the first script but then the first script does not work anymore. I guess I'm doing it the wrong way
const ss_2 = SpreadsheetApp.getActiveSpreadsheet();
const sheet_2 = ss_2.getSheetByName('Sales Report');
function insertColumn() {
const range_2 = sheet_2.getRange('C1:C150').getValues();
const newrange_2 = sheet_2.getRange('D1:D150');
const tmp_2 = new Date();
const yesterday_2 = new Date(tmp_2.setDate(tmp_2.getDate()-1));
sheet_2.insertColumnAfter(3);
newrange_2.setValues(range_2);
sheet_2.getRange('C2').setValue(yesterday_2).setNumberFormat("MMMM");
}
Any help would be highly appreciated !

First of all, try to rename one of the functions in your script since they both have the same name. Don't forget to hit save. Please take note that Google Apps Script can run one function at a time (not including triggers). You can toggle through your functions through the dropdown menu (between the "Debug" and "Execution Log" buttons) to change to the function you would like to run. Make sure that both functions have different names.

Related

I need help creating Install trigger to replace onEdit()

I have created a spreadsheet that receives form input, fiddles about with the data and then I want to view one row of data by selecting a record based on an id field.
I read and watch a lot of vids and found this brilliant video that showed me how to write a script for it.
I made a simplified spreadsheet SimplifiedDB2023
and a script
//DummyDatabse2023
//const ss = SpreadsheetApp.openById("11Fw0OmFpvkkfh11c95gBd7qEWifGJO6eUv38UwKRxNU")
//SimplifiedDB2023
//const ss = SpreadsheetApp.openById("1c-y_XUuK0L86qUUX471-GdBT-CN5GNTk4EGK7G57_94")
const ss = SpreadsheetApp.getActiveSpreadsheet()
const updateWS = ss.getSheetByName ("Form")
const mergeddataWS = ss.getSheetByName ("mergeddata")
const fieldRange = ["P5","G7","C24","C25","D7","C27","J7","I9","J9","H9","G9","F9","D9","D11","G11","G12","C38","E14","C40","C41","C42","C43","H5","R7","R12","P10","C48","C49","C50","C51","C52","C53","C54","C55","C56","P6","P12","P14","P16","P8","R8","D5"]
const searchCell = updateWS.getRange("E2")
function search() {
const searchValue = searchCell.getValue()
const data = mergeddataWS.getRange("A2:AQ").getValues()
const recordsFound = data.filter(r => r[42] == searchValue)
if(recordsFound.length === 0) return
fieldRange.forEach((f,i) => updateWS.getRange(f).setValue(recordsFound[0][i]))
}
which worlked beautifully. When I edit cell E2. (Basically choosing from a list), using the onEdit function,
SimplifiedScript2023
it selects all the information from that row using a filter in the search() function..
When I followed the exact same procedure for my more complicated and involved spreadsheet,
DUMMYDB2023
I ran into issue. The problem seems to be that the second script I wrote
( which I basically copied and pasted ) doesn't seem to be "bound" to the sheet, so I cannot use : const ss = SpreadsheetApp.getActiveSpreadsheet().
as I get an error :
TypeError: Cannot read properties of null (reading 'getSheetByName')
I looked into this and then when I changed
const ss = SpreadsheetApp.openById("11Fw0OmFpvkkfh11c95gBd7qEWifGJO6eUv38UwKRxNU")
It worked, but I am not able to use the onEdit function, which is vital, because I want any edits to cell E2, to trigger the function (search).
So my questions are - Why does the second version (- DummyDB2023) not work - I suspect this is because this more developed sheet has links to external sources like the forms, but even so, I am the owner of the spreadsheet and I followed the exact same procedure to create the script so it 'should' be bound, but apparently is not.
The second question is, if I must use the openById, I need to create an installable trigger. I thought this would be quite simple, but I cannot find any comprehensible resource that shows how to create an installable trigger for when a single cell is edited.
Can anyone help me write a script for a trigger that runs my search function when cell E2, on sheet Form! is edited?
As always, thanks for your time and help.
I created a stripped down version of my spreadsheet and the procedure worked perfectly.
When I applied it to my full spreadsheet, it wouldn'T run.

Google Apps Script copies spreadsheet every time I run the script

I'm trying to write a script that executes a function every time I open the spreadsheet the script is linked to.
But now every time I run the function, a copy of the spreadsheet is created and that definitely shouldn't happen.
I'm not sure if it might have to do with the triggers or type of deployment I set. I put a screenshot of the trigger here.
As the type of deployment I put web app.
The code in the script is pretty simple because I didn't want to go forward yet when the error is still happening. It looks like this:
function mediaplan() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s_week = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Quick view - Week");
var s_month = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Quick view - Month");
var test = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Test");
var currentWeek = Utilities.formatDate(new Date(), "GMT", "w");
console.log(currentWeek);
}
It would be great if someone could tell me what I did wrong or if it's an error by Google.
Thanks in advance!

Scripts Running Slowly

My scripts each take over a minute to run. They accept triggers from spreadsheet, so the longer they take to run, the more executions build up, and I've started getting failures due to "simultaneous invocations."
I'm running two AutoFill functions (see sample) OnChange, and one script that populates drop-down data validations OnEdit.
function accountCreationAutoFill(e){
Logger.log(e.changeType);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Account Creation Sheet")
if(e.changeType=='INSERT_ROW'){
var rules = sheet.getConditionalFormatRules();
var needsSourceRange = sheet.getRange("AccountCreationNeedsFormula")
var needsDestination = sheet.getRange("AccountCreationNeedsAutoFill")
var contactSourceRange = sheet.getRange("AccountCreationContactFormula")
var contactDestination = sheet.getRange("AccountCreationContactAutoFill")
needsSourceRange.autoFill(needsDestination, SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES)
contactSourceRange.autoFill(contactDestination,SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES)
sheet.clearConditionalFormatRules();
sheet.setConditionalFormatRules(rules);}
}
I haven't tried anything. I'm new to coding, and it took everything I have just to write the scripts and get them to work in the first place. I don't know how to troubleshoot this issue.
Combining all functions I wanted to run On Change into one script, so I'm only running one OnChange trigger, shortened average run time by 75%.

Can I duplicate a trigger with my new spreadsheet?

I have a new client template that I want to keep from being filled out prior to being duplicated. I've set a trigger to run a duplication and renaming function for the file when the file is opened.
function newRecord(){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var scheck = sheet.getName();
if (scheck=='#New Client Record'){
var file = DriveApp.getFilesByName('#New Client Record').next();
file.setName('New Client')
file.makeCopy('#New Client Record');
}
}
This checks to see that the file is the correct file before proceeding, changes the name of the template and then duplicates the file and renames it the old template name. I configured it in this way so that when we start filling out the data right away while we're talking on the phone with a client, we aren't sullying our original template which remains pristine. It works nicely except that the trigger from file #1 doesn't transfer to file #2 which takes on the new identity of the original template.
So my question is - can I duplicate the trigger as well? Or is there script that can open the duplicate file and close the template file to protect my form?
EDIT: I threw a bit more at this last night and tried to add an install trigger with limited effect (I probably sound like a boomer talking about smoking the drugs with this - I'm not a programmer and have only a rudimentary vocabulary for script). I added the following function, which is transferring, but does not seem consistent in its functionality (excusing the pun).
function createSpreadsheetOpenTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('newRecord')
.forSpreadsheet(ss)
.onOpen()
.create();
}
Thanks for any assistance any of you can provide on this. I've stumbled my way this far on my own with the archives, but I've finally gotten myself stuck.
How to get a form to create a new sheet with values from a template
Here is a quick example of how to carry this out. What you need to see this example in action:
A template sheet that looks like this:
A form that will have 3 "Short answers" that are required.
Client Name
Phone
Email
A standalone script (Drive Web app > New > More > Google Apps Script).
A folder into which all the newly created sheets will go.
At the top of the script (outside any function) you can define 3 global variables with the appropriate IDs:
const FORM_ID = ...
const TEMPLATE_ID = ...
const CLIENT_FOLDER_ID = ...
First, to set up an onFormSubmit trigger, you can do this by running the following function only once.
function createTrigger(){
const form = FormApp.openById(FORM_ID)
ScriptApp.newTrigger("formSubmitReceiver")
.forForm(form)
.onFormSubmit()
.create()
}
After which comes the main function that will:
Receive the formSubmit event and create a range of values from it.
In this example, it will generate a followUpDate that will be 7 days from the submission of the form.
Create a copy of the template file.
Fill the range B1:B4 with the relevant info.
Resulting in a new spreadsheet:
function formSubmitReceiver(e){
const itemResponses = e.response.getItemResponses()
const values = itemResponses.map( itemResponse => [itemResponse.getResponse()] )
const followUpDate = new Date()
followUpDate.setDate(followUpDate.getDate() + 7)
values.push([followUpDate])
const newFile = DriveApp.getFileById(TEMPLATE_ID).makeCopy()
const parentFolder = DriveApp.getFolderById(CLIENT_FOLDER_ID)
newFile.moveTo(parentFolder)
newFile.setName(values[0][0])
const newId = newFile.getId()
const newSpreadsheet = SpreadsheetApp.openById(newId)
const sheet = newSpreadsheet.getSheetByName("Sheet1");
const range = sheet.getRange("B1:B4")
range.setValues(values)
}
Depending on how complex you make your form, and what type of questions you choose (i.e multiple choice etc) this can get more complicated but hopefully it will give you a good idea of how to get something like this working. The simplest way is just to use "Short Answers" as this will just return a string. Also remember to make the questions "Required" if you don't want to handle empty values. Again, this all depends on how exactly you want to implement it and the complexity of the information involved!
References and Further Reading
FormResponse
getItemResponses()
ItemResponse
getFileById(id)
makeCopy()

Script that adds new row to sheets in selected AND another Google Spreadsheet

I'm working on a Spreadsheet to keep track of team member's project hours. I've created a Spreadsheet per team member for them to fill out weekly, and a project overview Spreadsheet that takes in all data through IMPORTRANGE.
To be able to quickly add a new project I want a macro to insert a new row in the Project overview + the separate Spreadsheets per team member. However I can't figure out how to write the correct code for the separate team member Spreadsheets. What's going wrong here?
If possible I'd also like to make a macro to DELETE a row in the project overview + team member spreadsheets, and one to HIDE a row...
Project overview
Team member Kate
Team member David
My current code:
function InsertRow() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
var row = SpreadsheetApp.getActiveRange().getRow();
// Array holding the names of the sheets to exclude from the execution
// I only managed to make it work when I exclude the sheet that I actually want to affect instead of the other way around?
var exclude = (["PROJECTS"] ||
SpreadsheetApp.openById("1xjR3lx5_KAA9nqiD3YsjZnulQaMyWGPQqgYsjtzQ0xI").getSheets() ||
SpreadsheetApp.openById("1Q5gtZlqf41of1Zwi8pvZbDx4NN5LcDh5SxfwasLUDMU").getSheets())
for(var s in allsheets){
var sheet = allsheets[s];
// Stop iteration execution if the condition is meet.
if(exclude.indexOf(sheet.getName())==-1) continue;
sheet.insertRowBefore(row);
}
}
As I see it you have a couple of options, which I'll be listing here as A, B, and C. Please note that you might need two different .GS files as you are linking to two sheets
A
Try code found on google app script documentation
I found the google apps script documentation for this command found here, so you might want to check that for this questions and others , but here is the exact code included
// The code below opens a spreadsheet using its ID and logs the name for it.
// Note that the spreadsheet is NOT physically opened on the client side.
// It is opened on the server only (for modification by the script).
var ss = SpreadsheetApp.openById("abc1234567");
Logger.log(ss.getName());
B
use open by url instead of open by id
Your issue might be that your current id isn't correct, I have no way of knowing, so here is some alternate code here (link to documentation here)
// The code below opens a spreadsheet using its id and logs the name for it.
// Note that the spreadsheet is NOT physically opened on the client side.
// It is opened on the server only (for modification by the script).
var ss = SpreadsheetApp.openByUrl(
'https://docs.google.com/spreadsheets/d/abc1234567/edit');
Logger.log(ss.getName());
C
Tie the google script to one sheet
This last option doesn't require any code, just an explanation. Instead of trying to link your script to two separate sheets, you might be able to automatically link it to a single google sheet and create two pages in the sheets file that you treat as two different sheets but are one thing. This might not be what you want, but I included it anyways. You link the sheet to the code automatically by:
1 opening your sheet
2 going to "tools"
3 clicking script editor
4 copy and paste your code (except for the "open by id" part)
5 success!
Your exclude variable doesn't contain what you think it does. You're using an "or" operator (||), which will take the first "truthy" value and skip the rest.
console.log((["PROJECTS"] || 'something else')); // ["PROJECTS"]
Moreover, you don't have a good way of telling which spreadsheet belongs to which team member. To solve that problem, you can create an object.
const teamSpreadsheetIds = {
'DAVID': 'ABC',
'KATE': '123',
};
console.log(teamSpreadsheetIds['DAVID']); // ABC
With the teamSpreadsheetIds object, you can now go about updating your team member sheets locally as well as their individual spreadsheets. The "PROJECTS" sheet is unique, so there's only one check for it.
function InsertRow() {
const ss = SpreadsheetApp.getActive();
const allSheets = ss.getSheets();
const row = SpreadsheetApp.getActiveRange().getRow();
const teamSpreadsheetIds = {
'DAVID': '1Q5gtZlqf41of1Zwi8pvZbDx4NN5LcDh5SxfwasLUDMU',
'KATE': '1xjR3lx5_KAA9nqiD3YsjZnulQaMyWGPQqgYsjtzQ0xI',
};
for (let sheet of allSheets) {
const sheetName = sheet.getName();
const memberSpreadsheetId = teamSpreadsheetIds[sheetName];
const isSkippable = memberSpreadsheetId === undefined && sheetName !== 'PROJECTS';
if (isSkippable) { continue };
// Insert a row in the local sheet
sheet.insertRowBefore(row);
// Get the member sheet and insert a row
if (memberSpreadsheetId) {
const memberSpreadsheet = SpreadsheetApp.openById(memberSpreadsheetId);
const memberSheet = memberSpreadsheet.getSheets()[0]; // Assumes the first sheet is the one to modify
memberSheet.insertRowBefore(row);
}
}
}