Google sheets app script setting formula / value onEdit - google-apps-script

I have the following code to try and set a lookup formula when a user deletes a cell by mistake:
function onEdit(e)
{
var ssA = SpreadsheetApp.getActive();
var ss = ssA.getSheetByName("Completed Work Requests")
var lastRow = ss.getLastRow();
var range = ss.getRange(5,6,lastRow,1);
var data = range.getValues();
for(var i=0;i<data.length;i++)
{
if(data[i][0] == "")//If true then it's blank
{
data[i][0]="=IFERROR(INDEX('Client lookup sheet'!C[-1]:C[-1],MATCH(R[0]C[-4],'Client lookup sheet'!C[-5]:C[-5],false)),\"\")"
range.setValue(data[i][0]);
}
}}
The problem I am having is the range.setValue(data[i][0]); part where I can see that it is setting the entire range to the value of data[i][0] in the previous line. The issue is that even if a user inputs a value manually, the onEdit function simply resets the value to data[i][0] = .... while I want them to be able to set the manual value without the function overwriting the value.
So my question boils down to what do I need to use instead of range.setValue(data[i][0]) to ensure only the cell evaluated by data[i][0] == "" is set to become a formula rather than the entire range?
Thanks!

I have fixed my issues by editing my code to:
edit: The previous issue was that I couldn't figure out a way to set value when an instance of data[i][0] == "" was found. The original code in the question above had the setValue applied to the entire range so when a user manually inputted a value, the setValue would simply reset the value to the formula. I have found that I can find the range of data[i][0] using getRange(i+5,6) as the row starts at 5 and column number = 6. This new range can then be used to setValue at the appropriate cell rather than the whole range
function onEdit(e)
{
var ssA = SpreadsheetApp.getActive();//changed from openById() for my convenience
var ss = ssA.getSheetByName("Completed Work Requests")
var lastRow = ss.getLastRow();
var range = ss.getRange(5,6,lastRow,1);
var data = range.getValues();
for(var i=0;i<data.length;i++)
{
if(data[i][0] == "")//If true then it's blank
{
data[i][0]="=IFERROR(INDEX('Client lookup sheet'!C[-1]:C[-1],MATCH(R[0]C[-4],'Client lookup sheet'!C[-5]:C[-5],false)),\"\")"
var rangedata = ss.getRange(i+5,6)
rangedata.setValue(data[i][0]);
}
}}

Related

Use e.oldValue with delete Key

Hello I am currently working on a time tracking system. With the following code I track the time how long a value was in a cell. This time is recorded in another worksheet and this is done continuously by appendRow ().
function onEdit(e) {
addTimestamp(e);
}
function addTimestamp(e) {
var ui = SpreadsheetApp.getUi();
var ws = "Tabellenblatt2";
var ss = e.source;
var targetSheet = ss.getSheetByName("Tabellenblatt1");
var range = targetSheet.getRange(3, 2, 1000, 1);
var currentDate = new Date();
if (e.source.getActiveSheet().getName() === ws && range != "") {
var val = e.range.getValue();
if (val != "") {
let rowToAdd = [val, "", currentDate, ""]
ss.getSheetByName("Tabellenblatt1").appendRow(rowToAdd);
ui.alert("Test2");
} else {
var sheet = ss.getSheetByName("Tabellenblatt1");
var dataFinder = sheet.createTextFinder(e.oldValue);
var nameRow = dataFinder.findAll()[0].getRow();
sheet.getRange(nameRow, 4).setValue(currentDate);
ui.alert("Test3");
}
}
}
Currently I cannot delete the content of the cell with the delete key, otherwise e.oldValue is undfined and I cannot get the date in the first worksheet. Now I always have to go to the formula area, mark the text in the cell and delete it. Is there a way to use the whole thing with the delete key anyway?
Unfortunately you cannot have the property oldValues if you use del or backspace button. According to Tanaike's comment here, there are certain scenarios where values and oldValues would appear in the event object.
In the case for editing a value to empty cell
e.value is included in e.
e.oldValue is NOT included in e.
In the case for overwriting a cell with a value by other value
Both e.value and e.oldValue are included in e.
In the case for deleting a value from a cell with a value
Both e.value and e.oldValue are NOT included in e.
Values of event object in multiple scenarios:
Adding value to an empty cell:
Overwriting a cell:
Deleting a value from a cell with a value:
A workaround would be creating a macro or button that will save and delete the value of the cell.
Example:
Code:
// this function will delete and display value of the active cell
function deleteCell(){
var sh = SpreadsheetApp.getActiveRange();
var oldValue = sh.getValue();
sh.clear();
Browser.msgBox(oldValue);
}
Button:
References:
Creating macros in Apps Script
Add A Google Sheets Button To Run Scripts

Get notes from a cell and insert into another cell

I have a range of cells in Google Sheets, some of them have notes attached.
If there's a note attached to a cell, I need to put the note in a separate cell, and put the location of that note in another cell.
I found this script elsewhere:
function getNote(cell)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getRange(cell)
return range.getNote();
}
but when I try to use it I get an error "Exception: Range not found (line 12)."
But this script only gets me halfway there as it only gets the note and puts it in a cell. I also need to know what cell the note came from.
Any help is greatly appreciated.
In the script above the function getNote() expects the paramter cell
If you just run the script without calling getNote() from another function / an environment where it gets a values for cell assigned, the script will fail wiht the error you obtained.
Indeed, this script doe snot not meet your needs. What you probably want is to screen all your cells for the one that have notes.
What you need to decide is into which cells you want to put the note and the cell notation.
Below is a sample that you need to adapt for your needs:
function getNotes() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
//if you have only one sheet in the spreadsheet, otherwise use ss.getSheetByName(name);
var sheet = ss.getActiveSheet();
var range = sheet.getDataRange();
var results = range.getNotes();
for (var i = 0; i < results.length; i++) {
for (var j = 0; j < results[0].length; j++) {
//if a not empty note was found:
if(results[i][j]){
var note = results[i][j];
var cell = range.getCell(i+1, j+1);
var notation = cell.getA1Notation();
//adjust the offset as function of the column / row where you want to output the results
cell.offset(0, 1).setValue(note);
cell.offset(0, 2).setValue(notation);
}
}
}
}
Important references:
getNotes()
getA1Notation()
getDataRange()
getCell(row, column)
offset(rowOffset, columnOffset)

google sheet script doesn't enter the second for loop

I'm doing a loop in google sheets script, i'm trying to find a row in the "Inventar" sheet that has the same value found in the sheet "Fisa client" and then change the value of a cell in that row but it appears that it does not enter the second for. I'm new to this so i don't know what's the problem.
function adaugabucati1(){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var ss=sheet.getSheetByName('Fisa client');
var ss2=sheet.getSheetByName('Inventar');
var data = ss.getRange("g9:l18").getValues();
var data1=ss2.getDataRange().getValue();
for(var i=0;i<data.length;i++){
if(!data[i][0] == ""){
var cod = ss.getRange(i+9,8).getValue();
var buc = ss.getRange(i+9,10).getValue();
for(var y=0;y<data1.length;y++){
if(data1[y][5] == cod){
var bucati = data[y][7]-buc;
ss2.getRange(y+1,8).setValue(bucati);
}
}
}
}
}
The problem is in this line:
var data1=ss2.getDataRange().getValue();
getValue() returns only one value - the value of the top-left cell in the range.
When the if statement following the second loop evaluates data1[y][5], it returns "undefined" and the rest of the second loop is bypassed.
You need to change line#6 to:
var data1=ss2.getDataRange().getValues();
Note getValue() - > getValues()

Google Sheets Script - Reference a specific cell in a row

I have a sheet where when I change a specific cell to "YES", I need a template sheet to be copied to a new version and named as per the value of a cell on the current row.
I'm having trouble working out how to get the value of the first cell in the row selected. This is what I have so far (I know this is wrong):
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var currentCell = sheet.getCurrentCell();
if (currentCell = "YES")
{
SpreadsheetApp.getActiveSpreadsheet().toast("New change control sheet added to workbook.","Change Control",15);
var sourceRow = ss.getActiveRange().getRowIndex();
var tabName = ss.getRange(cell,1).getValues();
ss.getSheetByName("CCTemplate").showSheet()
.activate();
ss.setActiveSheet(ss.getSheetByName('CCTemplate'), true);
ss.duplicateActiveSheet();
ss.setActiveSheet(ss.getSheetByName('CCTemplate'), true);
ss.getActiveSheet().hideSheet();
ss.setActiveSheet(ss.getSheetByName('Copy of CCTemplate'), true);
ss.getActiveSheet().setName("CC" & tabName);
}
}
Any ideas?
function onEdit(e) {
var sh=e.range.getSheet();
if(sh.getName()=='Your Sheet Name' && e.value=="YES") {
e.source.toast="New change control sheet added to workbook.","Change Control",15);
var tabName=sh.getRange(e.range.rowStart,1).getValue();
var tsh=e.source.getSheetByName('CCTemplate');
var csh=tsh.copyTo(e.source);
csh.setName('CC'+tabName);
}
}
You should avoid using activate in your scripts especially in simple triggers where you have to finish in 30 seconds. I think this code does the same thing that you intended for your code. One significant difference is that I use the information that comes in the event object that comes with the trigger. You should add the code Logger.log(JSON.stringify(e)) and then look at the logs you will see that there is a lot of information available to you which removes the need to run extra functions to get things like a spreadsheet.
Use event objects
onEdit offers among others the event objects range and value which are helpful to retrieve the range that has been edited and its value.
Also
When you want to a cell and compare it against a value, like in if (currentCell = "YES") - you need to retrive its value (either currentCell.getValue() or just event.value) and you need to use == instead of = for comparison.
Be careful with getValues() vs getValue(). The former gives you a 2D array and is not necessary if you want to retrieve the value of a single cell.
There is no need to set your sheet to active in order to change its name.
You can rewrite your code as following:
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var currentCell = event.range;
var value = event.value;
if (value == "YES")
{
...
var sourceRow = range.getRowIndex();
var tabName = ss.getRange(sourceRow, 1).getValue();
...
ss.getSheetByName('Copy of CCTemplate').setName("CC" + tabName);
}
}

Google Apps Script - set formula of active cell depending on value of another cell

I’m writing a custom function in Google Apps Script that, if a certain other cell contains a number, uses the value of that cell and several other cells to calculate a result. Otherwise, if that certain other cell does not contain a number, the function should just return an empty string so that the active cell appears blank.
I was able to come up with a working function to do this, but in order to protect sensitive information, I’m not going to copy it here. Instead, here’s an example function that accomplishes the same thing:
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var rowNum = sheet.getActiveCell().getRow();
var rowVals = sheet.getRange(rowNum, 1, 1, 15).getValues();
var fVal = rowVals[0][5];
if (fVal == "" || isNaN(fVal)) {
return ""; //leave cell blank if column F doesn't contain a number
}
var aVal = rowVals[0][0];
var bVal = rowVals[0][1];
var cVal = rowVals[0][2];
var gVal = rowVals[0][6];
return ((gVal * fVal) + aVal + bVal + cVal);
}
However, in an effort to speed it up (and also some other reasons that would be complicated to try to explain here, so you'll have to just trust me), I want to have the custom function set the value of the cell to be a formula instead of doing the calculating itself. It doesn’t work to just put the formula in the cell in the first place because then it still calculates/shows a result even if column F doesn’t contain a number.
Here’s what I’ve tried so far:
function myFunction2() {
var sheet = SpreadsheetApp.getActiveSheet();
var rowNum = sheet.getActiveCell().getRow();
var fVal = sheet.getRange(rowNum, 6).getValue();
if (fVal == "" || isNaN(fVal)) {
return ""; //leave cell blank if column F doesn't contain a number
}
var formula = '=SUM((G2*F2)+A2+B2+C2)';
return formula;
}
^This just makes the cell display the string “=SUM((G2*F2)+A2+B2+C2)”.
So I then tried using setFormula on the active cell:
function myFunction3() {
var sheet = SpreadsheetApp.getActiveSheet();
var cell = sheet.getActiveCell();
var rowNum = cell.getRow();
var fVal = sheet.getRange(rowNum, 6).getValue();
if (fVal == "" || isNaN(fVal)) {
return ""; //leave cell blank if column F doesn't contain a number
}
cell.setFormula('=SUM((G2*F2)+A2+B2+C2)');
}
^which, when I called the function in a cell, returned an error saying “You do not have permission to call setFormula”. The same thing happened when I tried getting the a1 notation of the active cell and then using getRange(a1Notation).setFormula('=SUM((G2*F2)+A2+B2+C2)') instead of calling setFormula directly on the active cell.
Anybody know if there's a way around that permission error? or have any other ideas for how to accomplish this?
The permission error is because of restrictions on what user defined functions can do. You can, however, do this with onEdit like this:
function onEdit(e) {
var ss=SpreadsheetApp.getActiveSpreadsheet()
var s=ss.getActiveSheet();
var col = e.range.getColumn();
var rowNum = e.range.getRow();
if(col==6){
var fVal = s.getRange(rowNum,col,1, 1).getValue()
}
if (fVal == "" || isNaN(fVal)) {
return
}
else{
s.getRange(rowNum,col,1, 1).setFormula('=SUM((G2*F2)+A2+B2+C2)');
}}
I actually ended up figuring out a way to accomplish what I wanted using the built-in IF function, so that's what I did.