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");
}
};
Related
I have a changelog script that only collects my username and does not collect for any other user. I'm trying to figure out the bug but unable to find a solution.
function changelog_script(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetNameTracker = ss.getActiveSheet().getName();
if (sheetNameTracker !== "Changelog") {
let sheet = ss.getSheetByName('Changelog');
var range = e.range && e.range.getA1Notation();
var user = e.user.getUsername();
var function_source = "changelog_script";
var time = new Date();
var changeType = e.changeType || 'EDIT';
console.log(user);
var oldValue = e.oldValue;
var newValue = e.value;
var lastRow = sheet.getLastRow();
sheet.getRange(lastRow + 1, 1, 1, 8).setValues([
[time.toLocaleString(), function_source, changeType, sheetNameTracker, range, user, oldValue, newValue]]);
}
}
function onChange(e) {
if (e.changeType == "EDIT") return;
changelog_script(e);
}
Example Google Sheet with attached App script.
Thanks in advance. 🙏
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]);
}
}
I have the below script that adds a timestamp when a new row of data is added.
function onEdit(e) {
var s = SpreadsheetApp.getActiveSheet();
if(s.getName() !== 'Sheet7' || e.range.columnStart != 1 || e.range.rowStart < 1) return;
e.range.offset(0, 1).setValue(e.value ? new Date() : "");
}
I tried the below for adding the checkbox, doesnt work.
function AddCheckBox() {
var cell = SpreadsheetApp.getActive().getDataRange('A1:B');
var criteria = SpreadsheetApp.DataValidationCriteria.CHECKBOX;
var rule = SpreadsheetApp.newDataValidation()
.requireCheckbox()
.build();
cell.setDataValidation(rule);
}
I would like it to also add a checkbox as well to the column next to where the timestamp appears.
Thanks in Advance
I just worked it out if anyones interested
function onEdit(e) {
var s = SpreadsheetApp.getActiveSheet();
if(s.getName() !== 'TEST' || e.range.columnStart != 1 || e.range.rowStart < 1)
return;
e.range.offset(0, 1).setValue(e.value ? new Date() : "");
var spreadsheet = SpreadsheetApp.getActive();
var cell = SpreadsheetApp.getActive().getRange('C1');
var criteria = SpreadsheetApp.DataValidationCriteria.CHECKBOX;
var rule = SpreadsheetApp.newDataValidation()
.requireCheckbox()
.build();
cell.setDataValidation(rule);
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('C1').activate();
spreadsheet.getCurrentCell()
.getNextDataCell(SpreadsheetApp.Direction.DOWN).activate();
spreadsheet.getActiveRange()
.autoFillToNeighbor(SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES);
spreadsheet.getRange('A1').activate();
}
I created a library ('CustomerReportLibrary') that has a few functions for which I created installable onEdit triggers from the spreadsheet script.
Two specific functions from this library don't work (the others do..). Can anybody shed light about the reason for that?
The functions that don't work:
function restoreRangeSettings(e) {
// retrieves data validation, conditional formatting and formatting settings from template
var spreadsheet = SpreadsheetApp.getActive();
var range = e.range;
var sheet = range.getSheet();
var A1Notation = range.getA1Notation();
var templateRange = spreadsheet.getSheetByName(sheet.getName() + ' design').getRange(A1Notation);
templateRange.copyTo(range, SpreadsheetApp.CopyPasteType.PASTE_FORMAT, false);
templateRange.copyTo(range, SpreadsheetApp.CopyPasteType.PASTE_CONDITIONAL_FORMATTING, false);
templateRange.copyTo(range, SpreadsheetApp.CopyPasteType.PASTE_DATA_VALIDATION, false);
}
function logTimestamp(e) {
// logs task last edit timestamp
var spreadsheet = SpreadsheetApp.getActive();
var sheet = e.range.getSheet();
var editedCell = sheet.getActiveCell();
var editedRow = editedCell.getRow();
var editedColumn = editedCell.getColumn();
var emailCell = sheet.getRange(editedRow,5);
var dateCell = sheet.getRange(editedRow,4);
var time = new Date();
time = Utilities.formatDate(time, "GMT+02", "dd/MM/yyyy HH:mm:ss");
// don't do anything if edited sheets are not task sheets
if ((sheet.getName() == 'instructions') || (sheet.getName() == 'generate report')) {return;}
// logs timestamp when the edited range is within the task documentation table
if ((editedColumn() > 7) && (editedRow > 4)) {
// adds user and month to also to next row when task is added
if ((sheet.getRange(editedRow,5,1,3).getValues() != '') && (editedColumn == 8) && (sheet.getRange(editedRow+1,4).isBlank())) {
sheet.getRange(editedRow+1,5,1,3).setValues(sheet.getRange(editedRow,5,1,3).getValues());
}
dateCell.setValue(time);
}
}
The code in the spreadsheet script:
function EditTriggers() {
var customerSsId = SpreadsheetApp.getActive().getId();
var customerSpreadsheet = SpreadsheetApp.openById(customerSsId);
ScriptApp.newTrigger('CustomerReportLibrary.restoreRangeSettings')
.forSpreadsheet(customerSpreadsheet)
.onEdit()
.create();
ScriptApp.newTrigger('CustomerReportLibrary.logTimestamp')
.forSpreadsheet(customerSpreadsheet)
.onEdit()
.create();
//works fine
ScriptApp.newTrigger('CustomerReportLibrary.showRowsAndComments')
.forSpreadsheet(customerSpreadsheet)
.onEdit()
.create();
//works fine
ScriptApp.newTrigger('CustomerReportLibrary.optShortcuts')
.forSpreadsheet(customerSpreadsheet)
.onEdit()
.create();
}
Thanks in advance!
I am happy to update that the issue is resolved!
First action (logTimestamp)
if ((editedColumn() > 7) && (editedRow > 4)) {
I accidently referred to editedColumn as a function and not a variable.
Second action (restoreRangeSettings)
I have chosen a different strategy for the required result, base on this answer. (See discussion about what I tried to achieve here.)
Third action (general)
I palyed a bit with variables defenition. For example - defining the range as "e.range" did not work, while defining it as "e.source.getActiveRange()" magically did.
Finally - the final code
function restoreRangeSettings(e) {
// retrieves data validation, conditional formatting and formatting settings from template
var editedSs = e.source;
var editedRange = editedSs.getActiveRange();
var editedSh = editedRange.getSheet().getName();
var templateSs = SpreadsheetApp.openById('10zVclYTCEOEwskUID4vxSPxKR_tb3RaR76eG7TfxLUE');
var templateSh = templateSs.getSheetByName(editedSh);
var copiedSh = templateSh.copyTo(editedSs).hideSheet();
var templateRange = editedSs.getSheetByName(copiedSh.getName()).getRange(editedRange.getA1Notation());
templateRange.copyTo(editedRange, SpreadsheetApp.CopyPasteType.PASTE_FORMAT, false);
templateRange.copyTo(editedRange, SpreadsheetApp.CopyPasteType.PASTE_CONDITIONAL_FORMATTING, false);
templateRange.copyTo(editedRange, SpreadsheetApp.CopyPasteType.PASTE_DATA_VALIDATION, false);
editedSs.deleteSheet(copiedSh);
}
function logTimestamp(e) {
// logs task last edit timestamp
var editedRange = e.range;
var editedSh = editedRange.getSheet();
var editedRow = editedRange.getRow();
var editedColumn = editedRange.getColumn();
var emailCell = editedSh.getRange(editedRow,5);
var dateCell = editedSh.getRange(editedRow,4);
var time = new Date();
time = Utilities.formatDate(time, "GMT+02", "dd/MM/yyyy HH:mm:ss");
// don't do anything if edited sheets are not task sheets
if ((editedSh.getName() == 'instructions') || (editedSh.getName() == 'generate report')) {return;}
// logs timestamp when the edited range is within the task documentation table
if ((editedColumn > 7) && (editedRow > 4)) {
// adds user and month to also to next row when task is added
if ((editedSh.getRange(editedRow,5,1,3).getValues() != '') && (editedColumn == 8) && (editedSh.getRange(editedRow+1,4).isBlank())) {
editedSh.getRange(editedRow+1,5,1,3).setValues(editedSh.getRange(editedRow,5,1,3).getValues());
}
dateCell.setValue(time);
}
}
Note: spreadsheet script did not change.
I have a Google Sheets shared with others. My sheet name is "OrderList". I am looking for a script that grabs the user's email address and inputs into another cell when a user edits the sheet. I have written the following code, however it only returns the sheet owner's Gmail address:
var COLUMNTOCHECK1 = 5;
var COLUMNTOCHECK2 = 6;
var DATETIMELOCATION1 = [0,2];
var DATETIMELOCATION2 = [0,3];
var SHEETNAME = 'OrderList'
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
if( sheet.getSheetName() == SHEETNAME ) {
var selectedCell = ss.getActiveCell();
if( selectedCell.getColumn() == COLUMNTOCHECK1) {
var email = Session.getActiveUser().getEmail();
var dateTimeCell =
selectedCell.offset(DATETIMELOCATION1[0],DATETIMELOCATION1[1]);
dateTimeCell.setValue(new Date());
var dateTimeCell1 =
selectedCell.offset(DATETIMELOCATION2[0],DATETIMELOCATION2[1]);
dateTimeCell1.setValue(email);
}
if( selectedCell.getColumn() == COLUMNTOCHECK2) {
var dateTimeCell =
selectedCell.offset(DATETIMELOCATION2[0],DATETIMELOCATION2[1]);
dateTimeCell.setValue(new Date());
}
}
}
Since you're running this onEdit(e) you can use Event Objects, these are great for capturing the data you're trying to achieve. You'll need to set up an onEdit trigger for your project to capture the email address though. The code below should achieve what you're expecting.
var COLUMNTOCHECK1 = 5;
var COLUMNTOCHECK2 = 6;
var DATETIMELOCATION1 = [0,2];
var DATETIMELOCATION2 = [0,3];
var SHEETNAME = 'OrderList'
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
if( sheet.getSheetName() == SHEETNAME ) {
var selectedCell = ss.getActiveCell();
if( selectedCell.getColumn() == COLUMNTOCHECK1) {
var email = e.user.getEmail();
var dateTimeCell =
selectedCell.offset(DATETIMELOCATION1[0],DATETIMELOCATION1[1]);
dateTimeCell.setValue(new Date());
var dateTimeCell1 =
selectedCell.offset(DATETIMELOCATION2[0],DATETIMELOCATION2[1]);
dateTimeCell1.setValue(email);
}
if( selectedCell.getColumn() == COLUMNTOCHECK2) {
var dateTimeCell = selectedCell.offset(DATETIMELOCATION2[0],DATETIMELOCATION2[1]);
dateTimeCell.setValue(new Date());
}
}
}
As you can see, I've change your var email to use an event object to capture the email address of the user that is editing the spreadsheet:
var email = e.user.getEmail();