I have a very frustrating problem on my hands and I turn to you for help once more. I had the onEdit() function below which, together with the auxiliary functions, worked fine.
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activeSheet = ss.getActiveSheet();
var activeCell = activeSheet.getActiveCell();
//Check if the sheet is a JOb sheet and the cell us the status cell
if ( activeSheet.getName().indexOf("Job ID") != -1 && activeCell.getRow() == 4 && activeCell.getColumn() == 15 ) {
var targetSheet = ss.getSheetByName('Active Jobs');
var jobRowNumber = findJobIdRow();
var sourceCell = activeSheet.getRange(4,15);
sourceCell.copyTo(targetSheet.getRange(jobRowNumber,16));
}
if (activeSheet.getName().indexOf("Job ID") != -1 && activeCell.getRow() == 2 && activeCell.getColumn() == 15){
var switchValue = activeCell.getValue();
switch (switchValue){
case "On hold (i)":
case "On hold (ii)":
case "On hold (iii)":
case "To be assigned":
//Write date to active jobs sheet
addDateToActive("TBC");
break;
case "In progress":
var newDate = Browser.inputBox("Please enter report out date, example 18-Aug-2017");
addDateToActive(newDate);
break;
//default:
//Browser.msgBox("GOTHERE");
}
}
}
function findJobIdRow() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var jobID = ss.getActiveSheet().getRange(2,1).getValue();
var column = ss.getSheetByName('Active Jobs').getRange(2,1,ss.getSheetByName('Active Jobs').getMaxRows()-2,1);
var values = column.getValues(); // get all data in one call
for(var ct = 0; ct < values.length-1; ct++){
if(values[ct][0] == jobID){
var ct = ct+2;
break;
}
}
return ct;
}
function addDateToActive(input){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activeSheet = ss.getActiveSheet();
var activeCell = activeSheet.getActiveCell();
var jobid = activeSheet.getRange(2,1).getValue().toString();
var activeJobSheet = ss.getSheetByName("Active Jobs");
var activeJobs = activeJobSheet.getRange(1,1,activeJobSheet.getLastRow(),1).getValues();
activeJobs = ColumnToArray(activeJobs);
var jobrow = activeJobs.indexOf(jobid)+1;
if (jobrow == -1){
Browser.msgBox("Job Id not preent on Active Jobs sheet");
}else{
activeJobSheet.getRange(jobrow,15).setValue(input);
}
}
Then I included some code in this script which was supposed to send out some e-mails to people if some dates were approaching today's date. There were some problems with that code because of authorization requirement so I moved it into it's own separate function and came back to the original script that is posted above. Now the problem I am facing is, although this script works fine if ran from the script editor, manually run from a drawing button in the spreadsheet, or is ran by a on edit trigger I set up from the "Current project triggers" menu, it will not do the entire script if the script is triggered by the onEdit() function name. It does the first bit where it copies the content of a cell across but not the second bit with the case switch.
The obvious fix would be to just set up the trigger from the "Current project triggers" but this onEdit detection needs to apply to anyone in my team that edits this sheet. If I set it up like that it will work for me but no one else from my team.
Any help would be appreciated.
Setting up the trigger via the function name onEdit() proving tricky I wrote this bit of code and attached it to a drawing in the sheet. Then instructed all the users to click it which set up the trigger for them. This solved the problem and the trigger works fine now.
function triggerSetUp(){
var sheet = SpreadsheetApp.getActive();
ScriptApp.newTrigger("enforcer").forSpreadsheet(sheet).onEdit().create();
}
Related
I want to create a script that moves data from one sheet to another when I mark it as completed in a particular column. Using some other code I found on the internet, I have this, but when I go in and change that status to completed nothing happens. The trigger page in google apps script says it's executing, but it isn't doing anything to the actual sheet. Here is the code:
function onEdit(e) {
if(SpreadsheetApp.getActiveSpreadsheet() == "Planner" && e.value == "Completed"){ //If the edit was on Planner marking the Status "Completed"
var spr = SpreadsheetApp.getActiveSpreadsheet();
var myRange = e.range.offset(0,-3,0,3).getValue() //get the information from Planner
//find the first row of Calendar where completed assignments is blank
var column = spr.getRange('O:O');
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct][0] != "" ) {
ct++;
ct++;
e.source.getSheetByName("Calendar").getRange(ct,15,1,3).setValues(myRange).getValues(); //copy the values from Planner to Calendar
e.source.getSheetByName("Planner").getRange(myRange).setValues("").getValues(); //delete values from Planner
;}
return (ct);
}
}
I assume something is wrong with it but I don't know what. I've never used apps script before so I honestly don't know what I'm doing. Here is the sheet:
Sheet
I want to move completed homework from the planner sheet to the calendar sheet when I change the status. Thanks so much for any help!!
EDIT:
I used lamblichus's code and it works great except that I still want to delete the data from the Planner Sheet after I move it. I tried this code and it didn't work:
function onEdit(e) {
const ss = e.source;
const range = e.range;
const sheet = range.getSheet();
if (sheet.getName() == "Planner" && e.value == "Completed") {
var otherData = range.offset(0,-3,1,3).getValues();
var currentClass = range.offset(0,-4).getMergedRanges()[0].getValue();
var [task,,date] = otherData[0];
var targetSheet = ss.getSheetByName("Calendar");
var targetRange = targetSheet.getRange("O1").getNextDataCell(SpreadsheetApp.Direction.DOWN).offset(1,0,1,3);
targetRange.setValues([[date,task,currentClass]]);
var initialSheet = ss.getSheetByName("Planner");
var initialRange = initialSheet.range.offset(0,-3,1,3);
initialRange.clearContent(); //delete values from Planner
}
}
Issues and solution:
There are several issues with your current code:
If you want to check the sheet name, you have to use Sheet.getName(). SpreadsheetApp.getActiveSpreadsheet() just returns the active spreadsheet, not sheet, and not its name anyway.
If you want to get values from multiple cells, you should use getValues(), not getValue().
The third parameter of offset corresponds to the number of rows of the resulting range. Therefore, it should not be 0.
The "Class" name is in a merged range, and only the top-left cell in a merged range includes the corresponding value. To get that value, you can use getMergedRanges and retrieve the first element in the resulting array. Since getValue() returns the value in the top-left cell of a range, it will return the "Class" name.
Code sample:
function onEdit(e) {
const ss = e.source;
const range = e.range;
const sheet = range.getSheet();
if (sheet.getName() == "Planner" && e.value == "Completed") {
var otherDataRange = range.offset(0,-3,1,3);
var otherData = otherDataRange.getValues();
var currentClass = range.offset(0,-4).getMergedRanges()[0].getValue();
var [task,,date] = otherData[0];
var targetSheet = ss.getSheetByName("Calendar");
var targetRange = targetSheet.getRange("O1").getNextDataCell(SpreadsheetApp.Direction.DOWN).offset(1,0,1,3);
targetRange.setValues([[date,task,currentClass]]);
otherDataRange.clearContent();
}
}
It looks like a syntax error on line 14, you put ;}, it should be }; you don't need to tell JavaScript (the coding language that AppScript is based on) when you end comments. But it likes it when you tell it when you end while loops.
Here is the updated code.
function onEdit(e) {
if(SpreadsheetApp.getActiveSpreadsheet() == "Planner" && e.value == "Completed"){ //If the edit was on Planner marking the Status "Completed"
var spr = SpreadsheetApp.getActiveSpreadsheet();
var myRange = e.range.offset(0,-3,0,3).getValue() //get the information from Planner
//find the first row of Calendar where completed assignments is blank
var column = spr.getRange('O:O');
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct][0] != "" ) {
ct++;
ct++;
e.source.getSheetByName("Calendar").getRange(ct,15,1,3).setValues(myRange).getValues(); //copy the values from Planner to Calendar
e.source.getSheetByName("Planner").getRange(myRange).setValues("").getValues(); //delete values from Planner
};
return (ct);
};
}
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.
I use this script to set a timestamp when a cell is changed. But it triggers in all sheets, and I only want it to be triggered when I do a change in sheet "XXX"
function onEditDatoMaxboStart() {
var s = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("XXX"); //change this to the name of your sheet
var r = s.getActiveCell();
if( r.getColumn() != 29 ) {
var row = r.getRow();
var time = new Date();
time = Utilities.formatDate(time, "GMT+01:00", "dd.MM.yyyy hh.mm.ss");
SpreadsheetApp.getActiveSheet().getRange('AP' + row.toString()).setValue(time);
};
};
How do I lock it to only be triggered when I make a change in the sheet "XXX"?
You cannot lock onEdit triggers to be ran on specific Sheets. However, within your onEdit function, you can check whether the Sheet that has triggered the action is the one you are targeting. If it is not, you can just return from the function without really executing any action. Your modified function would look as follows (edited only the first two lines):
function onEditDatoMaxboStart(e) {
if (e.range.getSheet().getName() !== "XXX") return;
var s = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("XXX"); //change this to the name of your sheet
var r = s.getActiveCell();
if( r.getColumn() != 29 ) {
var row = r.getRow();
var time = new Date();
time = Utilities.formatDate(time, "GMT+01:00", "dd.MM.yyyy hh.mm.ss");
SpreadsheetApp.getActiveSheet().getRange('AP' + row.toString()).setValue(time);
};
};
Also, I suggest you check out this page on using events objects obtained in trigger functions.
I would like for the following function to only run on my "Estimate" tab, but it runs on all tabs. I thought calling the sheet by name would resolve this, but it doesn't. I'm very new to GAS and have minimal coding experience, so I'm obviously just doing something wrong.
Here is a link to my sheet
function onEdit(){
//https://www.youtube.com/watch?v=8aOn0VMgG1w
//var ss = SpreadsheetApp.getActiveSpreadsheet();
//var estss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Estimate');
//commented out to try the var below...
//same result
var estss = SpreadsheetApp.getActive().getSheetByName('Estimate');
//var catss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Categories');
//commented out to try the var below...
//same result
var catss = SpreadsheetApp.getActive().getSheetByName('Categories');
var activeCell = estss.getActiveCell();
if(activeCell.getColumn() == 1 && activeCell.getRow() > 1){
activeCell.offset(0, 1).clearContent().clearDataValidations();
var cat = catss.getRange(1,2,1,catss.getLastColumn()).getValues();
var catIndex = cat[0].indexOf(activeCell.getValue()) + 2;
if(catIndex != 0){
var validationRange = catss.getRange(2, catIndex,catss.getLastRow());
var validationRule = SpreadsheetApp.newDataValidation().requireValueInRange(validationRange).build();
activeCell.offset(0, 1).setDataValidation(validationRule);
}
}
}
When selecting a category in column A on the "Estimate" tab, the following column (B) should set a data validation with all of the sub-categories (rows below the corresponding category), from the "Categories" tab. It basically works, but it also works on every other tab. I would like for it to work on the "Estimate" tab only.
Instead of the active methods, use the Event object
If your event object is e -> function onEdit(e){}
e.range returns the edited range, then you could use
e.range.getSheet().getName() to get the name of the active sheet
then you could use an if statement like
if(e.range.getSheet().getName() !== 'Sheet 1') return;
to stop the execution of the script when the active sheet's name isn't Sheet 1
I've been trying to deploy this web app bound to my shared spreadsheet. Basically it's a script that locks a cell after it's been edited. It is working just fine when I access it, however when I switch to another user and try, it doesn't function. You would think it would be a sharing permission setting, but I've changed everything to public and still no luck. Can you someone please help?
Here is the script:
function doGet(e) {
var sheet = SpreadsheetApp.openById('1234567891011');
}
function onEdit(){
var masterSheetName = "PlaceYourSquare" // sheet where the cells are protected from updates
var helperSheetName = "Helper" // sheet where the values are copied for later checking
var firstDataRow = 1; // only take into account edits on or below this row
var firstDataColumn = 1; // only take into account edits on or to the right of this column
var ss = SpreadsheetApp.getActiveSpreadsheet();
var masterSheet = ss.getActiveSheet();
if (masterSheet.getName() != masterSheetName) return;
var masterCell = masterSheet.getActiveCell();
if (masterCell.getRow() < firstDataRow || masterCell.getColumn() < firstDataColumn) return;
var helperSheet = ss.getSheetByName(helperSheetName);
var helperCell = helperSheet.getRange(masterCell.getA1Notation());
var newValue = masterCell.getValue();
var oldValue = helperCell.getValue();
if (oldValue == "") {
helperCell.setValue(newValue);
} else {
masterCell.setValue(oldValue);
}
}