Installable Triggers to use multiple onEdit - google-apps-script

I have created 2 functions that run onEdit (tested and working when named onEdit)
The first one sets the value (new Date) of a row >7 in Column D onEdit of Column C.
function CompleteTime() { //Function to add a time stamp to Complete Time Column D
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var range = sheet.getActiveCell();
var row = range.getRow();
var col = range.getColumn()
if(row>7 && col==3){
sheet.getRange(row, 4).setValue(new Date());
}
}
The second one sets a formula in Row 8 Column E onEdit of Row 8 Column D
function Duration() { //Function to set formula in column E to calculate duration from start time to first complte time
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var range = sheet.getActiveCell();
var row = range.getRow();
var col = range.getColumn()
if(row==8 && col==4){
sheet.getRange(row, 5).setFormula("=SUM(D8-B3)");
}
}
I have created 2 installable triggers to run these functions onEdit, and the first one runs fine, but the second one doesn't trigger the first one. The value that is entered by the first function doesn't trigger the second function.
Everything I've read suggests this is the way to get multiple onEdits to run in a single sheet, but I am stuck here.

One of the restrictions of triggers is that "Script executions and API requests do not cause triggers to run", meaning that you need to manually include the Duration() call after inserting the completion time.
The example below isn't the only way to accomplish this, but it should give you an idea of what I'm trying to describe.
function onEdit(e) {
var row = e.range.rowStart;
var col = e.range.columnStart;
if (row == 8 && col == 4) {
insertDurationFormula(e.range.offset(0, 1));
} else if (row > 7 && col == 3) {
insertCurrentTime(e.range.offset(0, 1));
insertDurationFormula(e.range.offset(0, 2));
}
}
function insertCurrentTime(cell) {
cell.setValue(new Date());
}
function insertDurationFormula(cell) {
cell.setFormula("=SUM(D8-B3)");
}
Also note that I'm using the event object included with edit triggers. Using the event object can help simplify your code a bit and reduce unnecessary calls.

It is not good practice to have multiple onEdit triggers in one spreadsheet
In case of simple onEdit triggers, it is not possible to have more than one per Apps Script project, in case of installable ones - it can cause to conflicts.
Instead, have only one function bound on a trigger and call from there on other function depending on the condition.
Sample:
function bindmeOnTrigger() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var range = sheet.getActiveCell();
var row = range.getRow();
var col = range.getColumn()
if(row==8 && col==4){
function1();
} else if(row>7 && col==3){
function2();
}
}
function function1(){
...
}
function function2(){
...
}
Or simply:
function bindmeOnTrigger() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var range = sheet.getActiveCell();
var row = range.getRow();
var col = range.getColumn()
if(row==8 && col==4){
sheet.getRange(row, 5).setFormula("=SUM(D8-B3)");
} else if(row>7 && col==3){
sheet.getRange(row, 4).setValue(new Date());
}
}

Related

Apps Script: Setfont italics for active row based on trigger cell

thanks in advance for any help.
I want to trigger italicizing the entire row if column 13 of the row contains "y" (ideally if there is a way to set font as normal if not containing "y" that would be grand too)
Here's what I tried (it's part of an onEdit nest). Currently it is only making the trigger column italics rather than the whole row as I am wanting.
function onEdit(e)
{
//Formula to run
{
spreadsheetWizard(e);
spreadsheetWizardII(e);
spreadsheetWizardIII(e);
moveRow(e);
}
}
function spreadsheetWizardIII()
{
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getActiveSheet()
var range = sheet.getActiveRange()
var editrow = range.getRow()
var trigger = sheet.getRange(editrow,13)
// Header Rows to ignore
if(range.getRow() <= 1) return;
// Sheet(s) to include / exclude
if(sheet.getName() !== 'Masterlist') return;
// Trigger Column
if(range.getColumn() != 13) return;
if(trigger.getValue() == "y")
{
editrow.setFontStyle('italic');
}
}

Combine two functions that must work one after another (with trigger)

I have two working functions. One of them is myFunction with a trigger. Is protects the row of the cell when any information is entered in this cell in Column4.
function myFunction(e) {
const sheetNames = ['Sheet1', 'Sheet2', 'Sheet3']; // Please set the sheet names you want to run the script.
const range = e.range;
const sheet = range.getSheet();
const value = range.getValue();
const row = range.getRow();
if (!sheetNames.includes(sheet.getSheetName()) || range.getColumn() != 4 || row == 2 || value == "") return;
const p = sheet.getRange(`B${row}:D${row}`).protect();
const owner = Session.getActiveUser().getEmail();
p.getEditors().forEach(f => {
const email = f.getEmail();
if (email != owner) p.removeEditor(email);
});
}
Another function is an onEdit function. It adds date in Column1 when I enter information in Column4. The date appears in the same row with the cell in Column4.
function onEdit() {
var colToCheck = 4;
// Offset from the input [row, column]
var dateOffset = [0, -3];
// Sheets to proceed on
var sheetNames = ['Sheet1', 'Sheet2', 'Sheet3'];
var sheet = SpreadsheetApp.getActive().getActiveSheet();
var name = sheet.getName();
if (sheetNames.indexOf(name) > -1) {
var cell = sheet.getActiveCell();
var col = cell.getColumn();
if (col == colToCheck) {
var dateTimeCell = cell.offset(dateOffset[0], dateOffset[1]);
dateTimeCell.setValue(new Date());
}
}
}
How these two functions can be combined in one sheet?
I just added two separate functions to one sheet: the one with trigger and the one onEdit as two different codes. And they work as I need. So, we do not need to combine them somehow. They just work one after another. First, the one onEdit function works, and as it adds info to the necessary cell, the function with trigger starts working.

Run trigger script when a one or more cells change in a range

I'm new to google sheets & script app. I'm using google sheets to maintain real-time project budgets. I've created a history tab to capture historical high level information (project code, category, Total Approved Budget, Projected Cost, Date stamp). Each time the scrip runs, it captures 4 rows of data.
function recordHistory() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('ForMaster');
var sheet2 = ss.getSheetByName('History');
var data = sheet1.getRange('S84:W87').getValues();
var last_row = sheet2.getLastRow();
sheet2.getRange(last_row + 1,1,4,5 ).setValues(data);
}
I'd like to set up a trigger that runs where ever there is a change to any of the cells in data range (S84:W87).
I've tried the following, but it doesn't seem to work.
function setupTrigger() {
ScriptApp.newTrigger('recordHistory')
.forSpreadsheet('1TSX27lzmOpv6P8Z8zJJhYem_yLQwP-gYK4WYHJ3LEuA')
.onEdit()
.create();
}
function recordHistory(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('ForMaster');
var sheet2 = ss.getSheetByName('History');
var data = sheet1.getRange('S84:W87').getValues();
var last_row = sheet2.getLastRow();
sheet2.getRange(last_row + 1,1,4,5 ).setValues(data);
}
Explanation:
You don't need an installable trigger since you don't use any service that requires permission.
You can use a simple onEdit(e) trigger instead.
In order to define the range S84:W87 that you would like to accept edits, you can do that:
row>=84 && row <= 87 && col >=19 && col <=23
84 is the starting row, 87 is the end row, column 19 is S and column 23 is W.
Also you want to only accept edits on the sheet ForMaster.
Therefore the if statement would look like that:
if(as.getName()=="ForMaster" && row>=84 && row <= 87 && col >=19 && col <=23)
Solution:
function onEdit(e) {
const ss = e.source;
const as = ss.getActiveSheet();
const row = e.range.getRow();
const col = e.range.getColumn();
if(as.getName()=="ForMaster" && row>=84 && row <= 87 && col >=19 && col <=23){
const sheet2 = ss.getSheetByName('History');
const data = as.getRange('S84:W87').getValues();
const last_row = sheet2.getLastRow();
sheet2.getRange(last_row + 1,1,data.length,data[0].length).setValues(data);
}
}
Note that!
The onEdit triggers are triggered upon user edits. This function won't be triggered by formula nor another script.
Again, this is a trigger function. You are not supposed to execute it manually and you will actually get errors. All you have to do is to save this code snippet to the script editor and then it will be triggered automatically upon edits.

Apps Script script in Google Sheets moving the wrong row with two onEdit triggers

I am using 2 functions in a tracker that I have. 1 function is for sorting column C by date (earliest first) automatically when a cell in that column is edited. If the word 'done' or 'void' is typed into that cell then the entire row is copied onto the next sheet.
However my issue is that when I type 'done' or 'void' into this cell, Google sheets is sorting the list and also processing the move function and thus moving the wrong row (the resulting row number, after the sort). How can i fix or improve the code.
function movedonevoid(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
var value = r.getValue();
if(s.getName() == "tracker" && r.getColumn() == 3 && (value=="done" || value=="void" )) {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("tracker2");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
s.deleteRow(row);
}
}
and
function sorttracker(event){
var sheet = event.source.getActiveSheet();
if(sheet.getName() == 'tracker'){
var editedCell = sheet.getActiveCell();
var columnToSortBy = 3;
var tableRange = "A2:D"; // What to sort.
if(editedCell.getColumn() == columnToSortBy) {
var range = sheet.getRange(tableRange);
range.sort( { column : columnToSortBy, ascending: true } );
}
}
}
I've inserted the above 2 bits of code as 2 different scripts and then used project triggers for both 'on edit'. Not sure if that is the best approach?
As you already noticed, having two different functions acting over the same range and being called by two different onEdit triggers isn't a good idea.
Instead of using two onEdit triggers, use only one to call an orchestral conductor function that will call the original two. The orchestral conductor function could be something like the following:
function onEdit(e) {
movedonevoid(e);
sorttracker(e);
}

Two OnEdit functions not working together [duplicate]

This question already has an answer here:
Merging or Combining two onEdit trigger functions
(1 answer)
Closed 1 year ago.
I got two onEdit() function scrips on a Google spreadsheet. But seems like one function is working at a time.
First function is a script to color all Rows, and second one is date function for adding dates on 2 cells based on column edit.
Here are the scripts.
function colorAll()
{
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 4;
var endRow = sheet.getLastRow();
for (var r = startRow; r <= endRow; r++) {
colorRow(r);
}
}
SpreadsheetApp.flush();
function colorRow(r)
{
var sheet = SpreadsheetApp.getActiveSheet();
var dataRange = sheet.getRange(r, 1, 1, 32);
var data = dataRange.getValues();
var row = data[0];
SpreadsheetApp.flush();
if(row[14] === ""){
dataRange.setBackgroundRGB(255, 255, 255);
dataRange.setFontColor("#000000");
}
else if(row[14] === "BEING USED") {
dataRange.setBackgroundRGB(150, 185, 255);
dataRange.setFontColor("#004BE1");
}
}
function onEdit(event)
{
var r = event.source.getActiveRange().getRowIndex();
if (r >= 2) {
colorRow(r);
}
}
function onOpen(){
colorAll();
And the second function.
function onEdit(e) {
var aCell = e.source.getActiveCell(), col = aCell.getColumn();
if(col == 19 || col == 21) {
var adjacentCell = aCell.offset(0, 1);
var newDate = Utilities.formatDate(new Date(),
"GMT+1", "dd/MM/yyyy");
adjacentCell.setValue(newDate);
}
}
The date function is working but the colorRow function is not, if I remove the date script then the colorRow will work.
Can any one point me in the right direction? Seems like I am missing something
Thanks
Barry Smith's comment about "two functions with same name" is right; only the second would execute in that case.
You can have just one spreadsheet-contained function named onEdit(). If you want to use another function as an onEdit trigger, you need to set it up as an installable trigger.
You can use the dialog from Resources -> Current Project's Triggers to install the second trigger.
Alternatively, you could have just one onEdit() simple trigger, but have it call the "sub-trigger" functions, passing the event object e to each of them.
Background: Guide to Triggers.
I might be misunderstanding, but it looks like you have two different functions with the exact same name. This can't happen, because the second one is basically overwriting the first one. Give the different names and it should work.
Found a very simple solution for this, just add the below to the start of your script:
function onEdit(e){
onEdit1(e);
onEdit2(e)
}
I rename my onEdit accordingly e.g. instead of onEdit1(e) I'll use hideRow(e) or whatever is relevant.
Here's a full code using three onEdit functions (automatically adding time to an edited cell, auto hiding a row when using a tick box and auto inserting a row):
function onEdit(e) {
autoTime(e);
hideRow(e)
}
function autoTime(e) {
var s = SpreadsheetApp.getActiveSheet();
if (s.getName() == "Sheet1") {
var r = s.getActiveCell();
if (r.getColumn() == 2) {
var nextCell = r.offset(0, 1);
if (nextCell.getValue() === '')
nextCell.setValue(new Date());
}
}
}
function hideRow(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
if (e.range.columnStart != 17 || e.value != "TRUE") return;
SpreadsheetApp.getActiveSheet().hideRows(e.range.rowStart);
}
function insertRow(e) {
var sheet = e.range.getSheet();
var lastRow = sheet.getLastRow();
if (e.range.getRow() > lastRow - 5)
sheet.insertRowAfter(lastRow - 1);
}