Google Sheets Copy down formula with inserting a row - google-apps-script

I am transitioning from Excel and VBA to Google Sheets and admittedly, don't know JavaScript.
I see that there is a common request for some VBA which will copy down the formulas from the row above, when I insert a new row anywhere in the dataset. I know how to add a butt, and i used the Macro Recorder to record the insertion of a Row, but the recorder only records that particular row that I chose to insert above.
I'd like the script to grab the activecell row number, so that it is dynamic on where ever my cursor is.
Here's the code I have so far. Can someone help me correct it?
function test() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getActiveCell().getRow().activate();
spreadsheet.getActiveSheet().insertRowsBefore(spreadsheet.getActiveCell().getRow(), 1);
spreadsheet.getActiveCell().offset(0, 0, 1, spreadsheet.getActiveCell().getNumColumns()).activate();
};

Solution:
You can accomplish your goal by creating an installable onChange trigger.
Follow the instructions below to set it up. After the setup is completed, you don't execute myFunction manually. It will be executed automatically everytime you insert a new row in the active sheet.
function myFunction(e) {
const sh = SpreadsheetApp.getActiveSheet();
if(e.changeType === 'INSERT_ROW') {
const row = sh.getActiveRange().getRow();
const formulas = sh.getRange(row-1,1,1,sh.getLastColumn()).getFormulasR1C1();
sh.getRange(row,1,1,sh.getLastColumn()).setFormulasR1C1(formulas);
}
}
Instructions:
Go to use the aforementioned code, copy/paste it to the script editor and click save.
Click on the current project's triggers:
Click on add trigger on the bottom right side of the page.
And then use exactly these settings:

Related

Trigger bug when copying rows

I have a function that copies rows from one sheet to another, which works when I run it manually:
function updateDataRange() {
var formSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1");
var lastFormRow = formSheet.getLastRow();
var dataSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DataRange");
dataSheet.clear();
for(var rowCounter = 1; rowCounter <= lastFormRow; rowCounter++) {
var sourceRange = formSheet.getRange('B'+rowCounter+':H'+rowCounter);
var insertRow = dataSheet.getLastRow() + 1;
var targetRange = dataSheet.getRange('A'+insertRow);
sourceRange.copyTo(targetRange);
}
}
However, when I run this via a trigger (e.g. function onEdit(e) { updateDateRange(); } ), there are gaps in the rows and some of the values will be left out. In this case, I know there is a "fix" by using rowCounter instead of insertRow to write the files instead of using getLastRow(), but one can easily see how this is a problem in another scenario.
So I guess my question is simple: Why isn't this working correctly when using a trigger?
Edit: To clarify, I have a 3rd sheet with some conditions/cells (i.e. conditions that influence which rows are to be copied) and changing them (like changing a different date) trigger the function.
Explanation:
You are using an onEdit trigger to capture sheet changes. But onEdit triggers work only when the user changes the value of a cell. Referencing the official documentation:
The onEdit(e) trigger runs automatically when a user changes the value
of any cell in a spreadsheet.
In your case, I think you are trying to execute this code when the Form Responses sheet is filled in with data by the form.
There are two kind of trigger functions that work with form submissions and they are both installable.
One is a google sheet event based trigger and the other one a form event based trigger.
Your goal is to execute some code from the google sheets side, so it makes sense to use the first one.
Modifications:
Change the name of the function from onEdit to a different name of your choice e.g. myFormSubmitTrigger.
Since the trigger is installable you need to "install" a onFormSubmit trigger for the myFormSubmitTrigger function. Here you can find some simple instructions on how to do that.
If your question is why doesn't your function work when the form makes changes to sheet Form Responses 1 then Marios has answered you question accept it and move on. If your trying to run your function when a user is making edits to sheet Form Responses 1 using a simple trigger then a possible explanation for not getting all of the rows completely copied is that simple trigger must complete in 30 seconds. If you continue to accept more data in Form 1 Responses then soon or later you will have problems but with this function it will be a lot later because it will run much faster than your code:
function updateDataRange() {
const sss=SpreadsheetApp.openById('ssid');
const ssh=e.source.getSheetByName('Form Responses 1');
const sr=2;
const svs=ssh.getRange(sr,2,sh.getLastRow()-ssr+1,7).getValues();
const dsh=e.source.getSheetByName('DataRange');
dsh.clear();
dsh.getRange(1,1,svs.length,svs[0].length).setValues(svs);
}
However, I would recommend that you never edit a form responses sheet. I would consider using the onFormSubmit trigger to capture all of the data to a second sheet that is available to edit. And it will have additional data automatically appended to it via dsh.appendRow(e.values). And so now you would no longer require an onEdit trigger because your data sheet is kept upto data with an onFormSubmit trigger and you may feel free to edit the datasheet any time you wish.
If neither of these questions suffices then I would recommend that you be more clear about what you are asking.

Google Sheet Script not fully executing

I'm using a Google Form to trigger this script.
When I run the script with the Play button it works perfect.
When I let the onsubmit trigger run it, the check box populates fine but the setValue date does not.
I've also tried using setFormula but I get the same result.
function AddCheckBox_toSchoolLunchForm(F) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1")
var criteria = SpreadsheetApp.DataValidationCriteria.CHECKBOX;
var rule = SpreadsheetApp.newDataValidation().requireCheckbox().build();
var range = sheet.getRange(sheet.getLastRow(), 8);
range.clearContent();
range.setDataValidation(rule);
var FormulaCell = sheet.getRange(sheet.getLastRow(), 10);
FormulaCell.setFormula("=DATEVALUE(A" + (sheet.getLastRow()) + ")");
}
The end goal is to have column J populate with the shot formatted date from column A each time the form is submitted.
I need this format to run a countIfs on another sheet.
Another option would be to somehow embed a format tag into this CountIfs command so that they match.
=COUNTIFS('Form Responses 1'!J:J,A2,'Form Responses 1'!D:D,B2)
UPDATED ANSWER
If you have problems running a function on trigger while it works as intended without a trigger, check the following:
For heavy files / form data they can be a delay in populating the sheet with new form data.
To avoid conflicts, give the spreadsheet some time to populate the new row before retrieving sheet.getLastRow() or accessing data in the sheet.
You can dot is easily e.g. with Utilities.sleep().
Check that your trigger has been installed correctly. For this check that
the type of trigger is correct
the trigger is bound to the correct function
avoid conflicts by creating a new trigger when renaming the funciton.
Check that the problem is not due to trigger restrictions
This is a common error source when using simple triggers.
Check that account under whose authorization the trigger is running has permissions to edit the sheet /range. This is important to check when the trigger owner is a diferent person that the person who runs the script manualy.
Note that for setFormula() it is not necessary to incorporate the = into the formula
Note that DATEVALUE() will only return the expected result when the cell is formatted correctly as a date

Trigger a script when a formula changes a cell value

I'm using a Google script then sends out an email when a certain column in a Google sheet is changed. The information in the cell is either inputted manually, or completes using a formula based on information in other cells.
The script works fine when information is manually entered, but not when the formula runs. I've read up on it and realise that a formula calculation doesn't count as an edit, so how do I get the script to run?
It's currently set up to trigger from the spreadsheet when there's an edit.
Below is the part of my script that covers the column/cell in question.
function sendEmail() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var row = sheet.getActiveRange().getRow();
var cellvalue = ss.getActiveCell().getValue().toString();
if(sheet.getActiveRange().getColumn() == 12)
There's a lot more included in the script so I haven't copied everything onto here. Many thanks in advance.
There is no trigger that can run when a formula changes.
Try figure out, what conditions is your formula depends on:
if it is another cell, entered manually, then use those cell to trigger it's changes with onEdit
if the formula imports data from external source, use random or time functions, you'd better use onTime trigger.
if the formula uses importrange then go to the range you import and see the original range, return to step 1 → 2...

Script in Google Apps Script that copies values in a range to another sheet

I am working on a tool that will allow my company to track its financial investments.
I need to create a simple data entry form for users to input transaction data, which will then populate a master sheet which is the basis for the analyses the tool does. I cannot do this via Google Forms because the data entry form uses a lot of conditional formatting based on other data in the sheet.
I have uploaded a very simplified sheet to illustrate what I need: this
What I am looking for is a script that, upon clicking Submit in the Data Entry sheet, copies the values (NOT the formulas) in B12:E12 to the first empty row (in this case, row 8) in the "Master Table" sheet. Ideally, clicking "Submit" will also clear the data entry fields in C4:C7 in the "Data Entry" sheet.
I have looked through various forums for a solution but have not found anything that does exactly that. I am sorry to say I am a complete newbie at Google Apps Script, therefore I could not write my own code to share, which I am aware is customary when asking a question here.
If anyone could point me in the right direction regardless, it would be much appreciated. I am currently trying to learn JavaScript and Google Apps Script using online resources, but for this specific project, it would take too long for me to reach a level where I could help myself.
Thank you very much, GEOWill!
Your answer solved my problem and thanks to your comments, I was able to understand exactly what your code does. I changed the code only to remove the Menu (but thank you very much for showing me how that is done anyway) - I tied the function to a Submit button inserted as a drawing. I also added some code to clear the contents of the entry range after clicking the button (this was suggested by someone else).
The final code that I used is:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var entry_sheet = ss.getSheetByName("Data Entry");
var master_sheet = ss.getSheetByName("Master Table");
function mySubmit() {
var entry_range = entry_sheet.getRange("B12:E12")
var val = entry_sheet.getRange("B12:E12").getDisplayValues().reduce(function (a, b) {
return a.concat(b);
});
Logger.log(val);
master_sheet.appendRow(val);
entry_sheet.getRange("C4:C7").clearContent()
}
I hope this helps others with a similar problem! Love how supportive this community is. Thanks for helping out.
I would begin by using a menu function to run your code in (rather than a cell button). You could use a cell button, but I believe you need to insert an image and assign a javascript function to that image anyways.
Basically, you begin by going to tools, script editor. Create an onOpen function and create a trigger that runs the onOpen() function each time the spreadsheet is opened. Inside the onOpen() function, we create a menu into which a physical menu item (a kind of button) exists (called 'Submit'). Finally, we associate a function to this button (I called it mySubmit()).
Inside of the mySubmit() function is where most of the functionality you are looking for exists. At this point, it is just a matter of copying from one range of cells and pasting them to another range of cells. For this, you'll notice that I had to setup a few variables ahead of time (ss, entry_sheet, master_sheet, entry_range, and master_row).
One last thing, you may want to protect the Master table sheet because if someone accidentally edits a cell beyond the last one edited, the input row would be copied to that row (due to how the getLastRow() function operates).
Hope this helps!
var ss = SpreadsheetApp.getActiveSpreadsheet();
var entry_sheet = ss.getSheetByName("Data Entry");
var master_sheet = ss.getSheetByName("Master Table");
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('MyMenu')
.addItem('Submit', 'mySubmit')
.addToUi();
}
function mySubmit() {
var entry_range = entry_sheet.getRange("B12:E12");
var master_row = (master_sheet.getLastRow() + 1);
entry_range.copyValuesToRange(master_sheet, 1, 3, master_row, master_row);
}

Custom function throws a "You do not have the permission required to setValue" error

I am trying to set some value to a cell in a Google Spreadsheet:
function exampleFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range1 = sheet.getRange("A1");
var value1 = range1.getValue();
value1+=1;
range1.setValue(2);
return value1;
}
If I am trying to affect a cell with this function, this error appears:
You do not have the permission required to setValue. (line 10, file "ddd")
Do you know how I could make that possible? I actually want the affected cell to take the value of the cell A1 and increase the value of A1 by 1.
from the documentation :
Custom functions return values, but they cannot set values outside the cells they are in. In most circumstances, a custom function in cell A1 cannot modify cell A5. However, if a custom function returns a double array, the results overflow the cell containing the function and fill the cells below and to the right of the cell containing the custom function. You can test this with a custom function containing return [[1,2],[3,4]];.
reference : Custom Functions in Spreadsheets
It looks that you are using the above function as a custom function, in other words, it is called by cell formula on the Google Sheets UI, in the following way:
=exampleFunction()
Custom functions in Google Sheets have limitations like they can't be used to call Google Apps Script services that require permissions. The workaround is to use another mean to call the function:
Run it from the Google Apps Script Editor
Use a custom menu
Use a trigger
Also they could be called from dialogs and sidebars, Google Apps Script Web apps and by using the Google Apps Script execution API
It's just a little different than what we programmers think.
You can use setFormula in a Macro but not in a custom function.
Just create a simple macro from Tools > Macros > Record Macro, and then open the Script editor and change the Macro's code to your code...
Here is my Macro's code:
function SetFormula() {
var spreadsheet = SpreadsheetApp.getActive();
var formulaValue = spreadsheet.getRange('formulaText').getValue().toString();
spreadsheet.getRange('total').setFormula(formulaValue);
return formulaValue;
};
Then, to run your macro automatically (you can run that manually from Tools > Macros > YOUR-MACRO-NAME), just create a trigger as follows:
Open the Script Editor:
Then go to Triggers from the left side panel and tap on Add Trigger button:
Finally, create the trigger, select your Macro from the list (mine is SetFormula), select the Event Source as From SpreadSheet, the Event Type to On Edit, and save it.
That's it!
I named my ranges as FormulaText and total to be more flexible.
you can do that from here:
Custom functions do have permission limitations as noted above. They can run with a custom menu or you can insert an image and assign a custom script to it to use it like a button.
Using a Trigger is another way to accomplish something like this example, which makes it automatic.
A simple trigger in an App Script such as onSelectionChange(e) works without running into the permissions issue of putting a custom function into a cell. This trigger is newer than what was available in the original post. In the simple example below, cell A1 will turn white with an even integer and red with anything else. Granted, the speed at which the triggers fire may vary. It's not always as instantaneous as one might expect.
function onSelectionChange(e) {
const sheet = SpreadsheetApp.getActive()
var value1 = sheet.getRange("A1").getValue()
if(value1 % 2 == 0) {
sheet.getRange("A1").setBackground("#FFFFFF") //white
} else {
sheet.getRange("A1").setBackground("#FF0000") //red
}
}