Can't get onEdit to call a function? - google-apps-script

I'm trying to connect 2 spreadsheets, such that, I can perform a sumif function on one spreadsheet and get the result on the current one. I'm using onEdit installable trigger on function when since the simple trigger doesn't work. The dependant dropdown and also the sumif through a for loop works, but I can't seem to get the function when to call the function sum in order to set value on a cell. The sum function works separately and gives the result, but when executing onEdit it doesn't.
Also, is this the correct way to execute sumif? Is there any faster way(I require to execute it around 600-1000 times, here I kept it just 10 to test it out, which again is slow)?
My code follows:
'''
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var stck = SpreadsheetApp.openById("1WGbEo1Xr99HwHY_4ZaeTRIgCOuNcjVfqCzZx4dcQX4I");
var unisensor = stck.getSheetByName("Unisensor");
var ssOptions = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Product List");
var options1 = ssOptions.getRange(2,1,ssOptions.getLastRow() - 1,2).getValues();
function When(e){
var activeCell = e.range;
var val = activeCell.getValue();
var r = activeCell.getRow();
var c = activeCell.getColumn();
var ssName = activeCell.getSheet().getName();
if (ssName=="Stock" && r==2 && c==1){
ss.getRange(r,2).clearContent();
ss.getRange(r,2).clearDataValidations();
var filteredOptions = options1.filter(function(o){return o[0] === val});
var listToApply = filteredOptions.map(function(o){return o[1]});
console.log(listToApply);
var cell = ss.getRange(r,2);
applyValidationToCell(listToApply,cell);
}
if (ssName=="Stock" && r==2 && c==2){
var company = ss.getRange(r,1);
if(company ==="Unisensor"){
sum(val);
}
}
}
function applyValidationToCell(list,cell){
var rule = SpreadsheetApp
.newDataValidation()
.requireValueInList(list)
.setAllowInvalid(false)
.build();
cell.setDataValidation(rule);
}
function sum(val){
var res = 0;
var result = ss.getRange(2,3);
for (i =4;i<=10;i++){
var cell = unisensor.getRange(i,3).getValue();
var cellres = unisensor.getRange(i,9).getValue();
if( cell == val){
res+= cellres;
}
}
result.clearContent();
result.setValue(res);
Logger.log(res);
}
'''
Thanks in advance.

If when(e) is being executed without errors, but sum() is not called, this means that your if condition is not fulfilled
Troubleshoot by implementing the following logs:
function When(e){
var activeCell = e.range;
var val = activeCell.getValue();
var r = activeCell.getRow();
var c = activeCell.getColumn();
var ssName = activeCell.getSheet().getName();
if (ssName=="Stock" && r==2 && c==1){
ss.getRange(r,2).clearContent();
ss.getRange(r,2).clearDataValidations();
var filteredOptions = options1.filter(function(o){return o[0] === val});
var listToApply = filteredOptions.map(function(o){return o[1]});
console.log(listToApply);
var cell = ss.getRange(r,2);
applyValidationToCell(listToApply,cell);
}
if (ssName=="Stock" && r==2 && c==2){
console.log('if (ssName=="Stock" && r==2 && c==2)')
var company = ss.getRange(r,1);
if(company ==="Unisensor"){
console.log('company ==="Unisensor"')
sum(val);
}
else{
console.log('NOT company ==="Unisensor"')
}
}
else{
console.log('NOT if (ssName=="Stock" && r==2 && c==2)')
}
}
After the funciton executes on onEdit trigger - view the logs.

Related

how to use a project on more than one sheet

I have the code below in my project, that helps me autofill the timestamp in "datetime" column in "test" sheet.
I want it to work for other sheets too. But I couldn't get it to work. Any help?
var SHEET_NAME = 'test';
var DATETIME_HEADER = 'datetime';
function getDatetimeCol(){
var headers = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME).getDataRange().getValues().shift();
var colindex = headers.indexOf(DATETIME_HEADER);
return colindex+1;
}
function onEdit() {
var ss = SpreadsheetApp.getActiveSheet();
var cell = ss.getActiveCell();
var datecell = ss.getRange(cell.getRowIndex(), getDatetimeCol());
if (ss.getName() == SHEET_NAME && cell.getColumn() == 1 && !cell.isBlank() && datecell.isBlank()) {
datecell.setValue(new Date()).setNumberFormat("yyyy-MM-dd hh:mm");
}
};
If you want to have an onEdit() trigger runs on multiple sheets use installable trigger.
Take your code and put it in a standalone script, not bounded to a sheet.
Then create function to setup onOpen Trigger
/**
* Creates a trigger for when a spreadsheet opens.
*/
function createSpreadsheetOnOpenTrigger() {
var id = 'YOUR_SHEET_ID';
var ss = SpreadsheetApp.openById(id);
ScriptApp.newTrigger('NAME_OF_FUNCTION_TO_RUN')
.forSpreadsheet(ss)
.onOpen()
.create();
}
reference : link
Then you just have to change id to setup trigger for all sheets you want the code run.
In the code take care to change to get infomration regarding celle and sheet from the event object :
function functionToRunOnEdit() {
var sheet = **e.range.getSheet()**;
var cell = **e.range**;
var name = sheet.getName();
var datecell = ss.getRange(cell.getRowIndex(), getDatetimeCol(sheet));
if (SHEET_NAMES.includes(name) && cell.getColumn() == 1 && !cell.isBlank() && datecell.isBlank()) {
datecell.setValue(new Date()).setNumberFormat("yyyy-MM-dd hh:mm");
}
};
Reference : link
I have also changed the variable name ss into sheet becausse it is a sheet but not a spreadsheet.
var SHEET_NAMES = ['test', 'test2'];
var DATETIME_HEADER = 'datetime';
function getDatetimeCol(sheet){
var headers = sheet.getDataRange().getValues().shift();
var colindex = headers.indexOf(DATETIME_HEADER);
return colindex+1;
}
function onEdit() {
var sheet = SpreadsheetApp.getActiveSheet();
var cell = sheet.getActiveCell();
var name = sheet.getName();
var datecell = ss.getRange(cell.getRowIndex(), getDatetimeCol(sheet));
if (SHEET_NAMES.includes(name) && cell.getColumn() == 1 && !cell.isBlank() && datecell.isBlank()) {
datecell.setValue(new Date()).setNumberFormat("yyyy-MM-dd hh:mm");
}
};

Applying google script to all sheets except specific

I am very new to google script trying to resolve key issues in business flow.
Through search have come up with the below script for multi-level data validation for sheet Template.
How can I edit this script to run for all Sheets except specific ones (ex. Sheet1, Sheet 2)
I have found the option of excluding sheets with the code below, but not sure how to use it and where to input it
var excludes = [];
// if (excludes.indexOf(s.getName()) != -1) return;
Thanks you in advance
var mainwsMaster = "Template";
var MenuWSname = "Master Menu"
var ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(mainwsMaster);
var wsMenu = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(MenuWSname);
var Menu = wsMenu.getRange(2,1,wsMenu.getLastRow()-1,3).getValues();
var firstColumn = 8;
var secondColumn = 9;
var thirdColumn = 10;
function onEdit(e){
var activeCell = e.range;
var val = activeCell.getValue();
var r = activeCell.getRow();
var c = activeCell.getColumn();
var wsName = activeCell.getSheet().getName();
if(wsName === mainwsMaster && c === firstColumn && r > 6){
applyFirstLevelValidation(val,r)
} else if(wsName === mainwsMaster && c === secondColumn && r > 6){
applySecondLevelValidation(val,r);
}
}//end onEdit
function applyFirstLevelValidation(val,r){
if(val === ""){
ws.getRange(r,secondColumn).clearContent();
ws.getRange(r,secondColumn).clearDataValidations();
ws.getRange(r,thirdColumn).clearContent();
ws.getRange(r,thirdColumn).clearDataValidations();
} else {
ws.getRange(r,secondColumn).clearContent();
ws.getRange(r,secondColumn).clearDataValidations();
ws.getRange(r,thirdColumn).clearContent();
ws.getRange(r,thirdColumn).clearDataValidations();
var filteredOptions = Menu.filter(function(o){return o[0] === val });
var filteredpiata = filteredOptions.map(function(o){return o[1] } );
var cell = ws.getRange(r,secondColumn);
applyValidationToCell(filteredpiata,cell);
}
}
function applySecondLevelValidation(val,r){
if(val === ""){
ws.getRange(r,thirdColumn).clearContent();
ws.getRange(r,thirdColumn).clearDataValidations();
} else {
ws.getRange(r,thirdColumn).clearContent();
var firstLevelColValue = ws.getRange(r, firstColumn).getValue();
var filteredOptions = Menu.filter(function(o){return o[0] === firstLevelColValue && o[1] === val });
var filteredpiata = filteredOptions.map(function(o){return o[2] } );
var cell = ws.getRange(r,thirdColumn);
applyValidationToCell(filteredpiata,cell);
}
}
function applyValidationToCell(list,cell){
var rule = SpreadsheetApp
.newDataValidation()
.requireValueInList(list)
.setAllowInvalid(false)
.build();
cell.setDataValidation(rule);
}
You mentioned this code:
var excludes = [];
// if (excludes.indexOf(s.getName()) != -1) return;
for your code it would look like this instead:
var excludes = ["Sheet1","Sheet2","OtherSheetToExclude","ETC"];
if (excludes.indexOf(wsName) != -1) {return};
You would place those two lines just after the wsName variable is declared in your onEdit(e) script.
It's basically saying. "Take the name of the sheet you've just edited and look for it in this list i just made called "Excludes". If you find it in there, STOP running this function."
Try
var excludes = ['Sheet1','Sheet2'];
and then, change as following
if( excludes.indexOf(wsName) == -1 && c === firstColumn && r > 6)
for instance, focused on the onEdit function
var firstColumn = 8;
var secondColumn = 9;
var excludes = ['Sheet1','Sheet2'];
function onEdit(e){
var activeCell = e.range;
var val = activeCell.getValue();
var r = activeCell.getRow();
var c = activeCell.getColumn();
var wsName = activeCell.getSheet().getName();
if(excludes.indexOf(wsName) == -1 && c === firstColumn && r > 6){
activeCell.getSheet().getRange('A1').setValue('cas1')
} else if(excludes.indexOf(wsName) == -1 && c === secondColumn && r > 6){
activeCell.getSheet().getRange('A1').setValue('cas2')
}
}//end onEdit

Google Sheets script - code optimization - one script, two sheets, same workbook

I have one workbook with multiple sheets and I've been running some simple scripts on two of those sheets (those sheets are basically copies of one another).
Once script creates a dropdown based on the value entered in one of the cells, while the second script adds a timestamp based on when the new dropdown was edited.
Initially I had two onEdit functions running on the first sheet.
Then I created a copy of these functions, with small amendments, to run on another sheet(same workbook)
Given that I don't really know how to have a separate script per worksheet I now have a one script with 4 similar onEdit functions.
Since I'm a total newbie I'm sure there's a better, more efficient way to write this code.
I'd appreciate your help in optimizing this.
function onEdit(e) {
addTimestamp(e);
addTimestamp2(e);
Dropdown(e);
Dropdown2(e);
}
function addTimestamp(e) {
var startRow = 2;
var targetColumn = 10;
var ws = "Tracker1";
var row = e.range.get.Row();
var col = e.range.getColumn();
if(col === targetColumn && row >= startRow && e.source.getActiveSheet().getName() === ws) {
var currentDate = new Date();
e.source.getActiveSheet().getRange(row,11).setValue(currentDate);
}
}
function addTimestamp2(e) {
var startRow = 2;
var targetColumn = 10;
var ws = "Tracker2";
var row = e.range.get.Row();
var col = e.range.getColumn();
if(col === targetColumn && row >= startRow && e.source.getActiveSheet().getName() === ws) {
var currentDate = new Date();
e.source.getActiveSheet().getRange(row,11).setValue(currentDate);
}
}
function Dropdown(){
var tabLists = "StatusFlow";
var tabValidation = "Tracker1"
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var datass = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(tabLists);
var activeCell = ss.getActiveCell();
if(activeCell.getColumn() == 9 && activeCell.getRow() > 1 && ss.getSheetName() == tabValidation){
activeCell.offset(o,1).clearContent().clearDataValidations();
var makes = datass.getRange(1,1,1, datass.getLastColumn()).getValues();
var makeIndex = makes[0].indexOf(activeCell.getValue()) +1;
if(makeIndex !=0) {
var validationRange = datass.getRange(2, makeIndex, datass.getLastRow());
var validationRule = SpreadsheetApp.newDataValidation().requireValueInRange(validationRange).setAllowInvalid(false).build();
activeCell.offset(0, 1).setDataValidation(validationRule);
}
}
}
function Dropdown2(){
var tabLists = "StatusFlow";
var tabValidation = "Tracker2"
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var datass = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(tabLists);
var activeCell = ss.getActiveCell();
if(activeCell.getColumn() == 9 && activeCell.getRow() > 1 && ss.getSheetName() == tabValidation){
activeCell.offset(o,1).clearContent().clearDataValidations();
var makes = datass.getRange(1,1,1, datass.getLastColumn()).getValues();
var makeIndex = makes[0].indexOf(activeCell.getValue()) +1;
if(makeIndex !=0) {
var validationRange = datass.getRange(2, makeIndex, datass.getLastRow());
var validationRule = SpreadsheetApp.newDataValidation().requireValueInRange(validationRange).setAllowInvalid(false).build();
activeCell.offset(0, 1).setDataValidation(validationRule);
}
}
}
here is a more efficient way to replace the two addTimestamp functions:
function onEdit(e) {
var activeSheet = e.source.getActiveSheet();
var activeSName = activeSheet.getName();
var row = e.range.get.Row();
var col = e.range.getColumn();
if ((activeSName == 'Tracker1' || activeSName == 'Tracker2') && col === 10 && row >= 2) {
activeSheet.getRange(row, 11).setValue(new Date());
}
}
You could give the two Dropdown functions a shot and see if you can do something similar. Nothing like learning-by-doing. :-D
Just be careful that in onEdit you are passing e to these Dropdown functions. But not using them in these functions.

Make multiple dependent data validations update the dropdown when validation values are changed

I have the following script in place to populate three different columns with differing date ranges (based on a filter formula) that are dependent on the date entered in column AM. Right now this is set up to adjust the data validation dropdown list ONLY if the cell in column AM is edited. I'd like it to also adjust the data validation dropdown list if the validation values themselves change (aka if I edit the filter formula at all).
Any help would be greatly appreciated!
var mainWsName = "PRT_Tracker";
var appointmentWsName = "Visit_Windows";
var firstLevelColumn = 39;
var secondLevelColumn = 41;
var thirdLevelColumn = 45;
var fourthLevelColumn = 50;
var ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(mainWsName);
var wsWindows = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(appointmentWsName);
var appointment = wsWindows.getRange(2,1,wsWindows.getLastRow()-1,4).getValues();
function onEdit(e) {
var activeCell = e.range;
var val = activeCell.getValue();
var r = activeCell.getRow(); //return row of active cell
var c =activeCell.getColumn(); // return col of active cell
var wsName = activeCell.getSheet().getName();
if (wsName === mainWsName && c === firstLevelColumn && r >1) {
applyFirstLevelValidation(val,r);
} if(wsName === mainWsName && c === firstLevelColumn && r >1) {
applySecondLevelValidation(val,r);
} if(wsName === mainWsName && c === firstLevelColumn && r >1) {
applyThirdLevelValidation(val,r);
}
}
function applyFirstLevelValidation(val,r){
var filteredAppointment = appointment.filter(function(a){return a[0] === val});
var listToApply = filteredAppointment.map(function(a){return a[1]});
var cell = ws.getRange(r,secondLevelColumn);
applyValidationToCell(listToApply,cell);
}
function applySecondLevelValidation(val,r){
var filteredAppointment = appointment.filter(function(a){return a[0] === val});
var listToApply = filteredAppointment.map(function(a){return a[2]});
var cell = ws.getRange(r,thirdLevelColumn);
applyValidationToCell(listToApply,cell);
}
function applyThirdLevelValidation(val,r){
var filteredAppointment = appointment.filter(function(a){return a[0] === val});
var listToApply = filteredAppointment.map(function(a){return a[3]});
var cell = ws.getRange(r,fourthLevelColumn);
applyValidationToCell(listToApply,cell);
}
function applyValidationToCell(list,cell) {
var rule = SpreadsheetApp
.newDataValidation()
.requireValueInList(list)
.build();
cell.setDataValidation(rule);
}

Modifying a flexible Google Sheets onEdit script to accommodate a wider range of inputs

Below is a script that allows me to combine five different onEdit functions into a single function.
function onEdit(e){
if (e.range.getA1Notation() != "H12" || e.value != "submitResponse") return;
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
var m = sh.getSheetByName("Master");
ss.getRange(2,1,ss.getLastRow(),3).copyTo(m.getRange(m.getLastRow()+1,1,ss.getLastRow(),3));
ss.getRange('H12').clearContent();
e.range.clearContents();
}
I'm now hoping to make this script more flexible. See Sheet1 of my spreadsheet: https://docs.google.com/spreadsheets/d/1EoOIQxWyKWOvtlCrmJNI76FAxGhzgXrE4s0F05tw2MY/edit#gid=0
It would be great if instead of limiting myself to H12, I could use the entire column of H:H to submit each corresponding row. So when I change H2 to submitResponse, it copies/pastes A2:G2 to the last row of the Master. When I change H3 to submitResponse, it copies/pastes A3:G3 to the last row of the Master. And so forth.
I tried my hand at this but no luck on execution. I guess I'm missing something.
​function onEdit(e) {
var ss = e.source;
var s = ss.getActiveSheet();
var r = e.range;
var actionCol = 8;
var rowIndex = r.getRowIndex();
var colIndex = r.getColumnIndex();
var colNumber = s.getLastColumn()-1;
if (e.value == "submitResponse" && colIndex == actionCol) ;
var sourceRange = s.getRange(rowIndex, 1, 1, colNumber);
var targetRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Master").getRange(getLastRow()+1, 1, 1, colNumber);
sourceRange.copyTo(targetRange,SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
}
Try this:
function onEdit(e){
var sh=e.range.getSheet();
if (sh.getName()=='Sheet1' && e.range.columnStart==8 && e.range.rowStart>1 && e.value=="submitResponse") {
var msh=e.source.getSheetByName("Master");
msh.appendRow(sh.getRange(e.range.rowStart,1,1,7).getDisplayValues()[0]);
}
}
function onEdit(e){
var sh=e.range.getSheet();
if (sh.getName()=='Sheet1' && e.range.columnStart==8 && e.range.rowStart>1 && e.value=="Yes") {
e.range.setValue('');
var msh=e.source.getSheetByName("Master");
msh.appendRow(sh.getRange(e.range.rowStart,1,1,7).getDisplayValues()[0]);
}
}