I have a script which helps me to protect rows according to the date in Column A. If the date shows yesterday then the whole row becomes protected on open, and all the rows with dates before yesterday. The problem is, that though it should work for several sheets, it works only for the first mentioned sheet (GIDER). Could you please advise anything to make it work for several sheets?
function onOpen(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetNames = ['GIDER','ORTAK_KASA','PERSONELextra']; // put the names of the sheets you want to run the script
sheetNames.forEach(name=>{
var sh = ss.getSheetByName(name);
var dateRange = sh.getRange(6,1, sh.getLastRow()-2, 1);
var val = dateRange.getDisplayValues();
var curDate = Utilities.formatDate(new Date(), "GMT+3", "MM/dd/YYYY");
var protectRow;
//check if date is less than the current date
for(var i = 0; i < val.length; i++){
if(val[i][0]>=curDate){
protectRow = i;
break;
}
}
var protection = sh.getProtections(SpreadsheetApp.ProtectionType.RANGE);
//If protection exists, update else add new one.
if(protection.length > 0){
var range = sh.getRange(6, 1, protectRow, 18);
protection[0].setRange(range);
}else{
sh.getRange(6, 1, protectRow, 18).protect();
}
});
}
if you wish to use >= here:
for(var i = 0; i < val.length; i++){
if(val[i][0]>=curDate){
protectRow = i;
break;
}
}
Then you should be using new Date().valueOf() or new Date().getTime() so that you can compare them numerically.
Related
I have a workbook with 6 similar sheets. And I have one script which helps me to protect the rows according to date in column A. Each row has a date. The rows are sorted according to the date, and we add information with a new date and data almost every day. When the date is before today, the row becomes protected, so we can enter new info in the row with current date and the future.
So, for today the yellow rows are protected. Here is the link for the worksheet.
The script I use:
function onOpen(e) {
var sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("ISKUR");
var dateRange = sh.getRange(6, 1, sh.getLastRow()-2, 1);
var val = dateRange.getDisplayValues();
var curDate = Utilities.formatDate(new Date(), "GMT+3", "MM/dd/YYYY");
var protectRow;
//check if date is less than the current date
for(var i = 0; i < val.length; i++){
if(val[i][0]>=curDate){
protectRow = i;
break;
}
}
var protection = sh.getProtections(SpreadsheetApp.ProtectionType.RANGE);
//If protection exists, update else add new one.
if(protection.length > 0){
var range = sh.getRange(6, 1, protectRow, 13);
protection[0].setRange(range);
}else{
sh.getRange(6, 1, protectRow, 13).protect();
}
}
function onOpen(e) {
var sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var dateRange = sh.getRange(6, 1, sh.getLastRow()-2, 1);
var val = dateRange.getDisplayValues();
var curDate = Utilities.formatDate(new Date(), "GMT+3", "MM/dd/YYYY");
var protectRow;
//check if date is less than the current date
for(var i = 0; i < val.length; i++){
if(val[i][0]>=curDate){
protectRow = i;
break;
}
}
var protection = sh.getProtections(SpreadsheetApp.ProtectionType.RANGE);
//If protection exists, update else add new one.
if(protection.length > 0){
var range = sh.getRange(6, 1, protectRow, 13);
protection[0].setRange(range);
}else{
sh.getRange(6, 1, protectRow, 13).protect();
}
}
It seems that it doesn't work correctly for several pages. How can I protect rows using this script in several sheets, if all of them have a similar structure (date in column A)?
Issue:
You can't have multiple onOpen triggers.
Solutions:
Solution 1 (recommended): execute your code for multiple sheets like that:
This code assumes you can run the same code for every sheet you specify in the sheetNames array:
function onOpen(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetNames = ['ISKUR','Sheet1']; // put the names of the sheets you want to run the script
sheetNames.forEach(name=>{
var sh = ss.getSheetByName(name);
var dateRange = sh.getRange(6, 1, sh.getLastRow()-2, 1);
var val = dateRange.getDisplayValues();
var curDate = Utilities.formatDate(new Date(), "GMT+3", "MM/dd/YYYY");
var protectRow;
//check if date is less than the current date
for(var i = 0; i < val.length; i++){
if(val[i][0]>=curDate){
protectRow = i;
break;
}
}
var protection = sh.getProtections(SpreadsheetApp.ProtectionType.RANGE);
//If protection exists, update else add new one.
if(protection.length > 0){
var range = sh.getRange(6, 1, protectRow, 13);
protection[0].setRange(range);
}else{
sh.getRange(6, 1, protectRow, 13).protect();
}
});
}
Solution 2: create multiple functions for every sheet:
This solution is recommended in case you want to run different code for different sheets:
function onOpen(e){
onOpen1(e);
onOpen2(e);
}
function onOpen1(e) {
var sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
// rest of the code
}
function onOpen2(e) {
var sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("ISKUR");
// rest of the code
}
That solution becomes handy in case different sheets have different structure or you want to apply a unique logic for individual sheets.
I have a template sheet for group data entry. Most of the sheet is free entry, but there are title rows that I don't want edited so I have them protected. We have one of these tabs for each day of the month and a new Sheet for each month.
I want to copy the template 30-31 times depending on the month and have the title of the sheet be the corresponding date (MM.dd.yy ie: 11.02.20). I have the Date set in A2 (ie: 11/01/2020).
So far I tried combining a protections and a date change, but I keep getting variable errors and then sometimes it double creates sheets (like 11.06.20 and then stops).
This is the code I've tried (and edited and moved around a few times).
function duplicateSheetWithProtections() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var dateCell = "A2";
sheet = ss.getSheetByName('Template.01.20');
sheet2 = sheet.copyTo(ss).setName('11..20');
var N = 30;
var startDate = new Date(s.getRange(dateCell).getValue());
var day = startDate.getDate();
var month = startDate.getMonth();
var year = startDate.getFullYear();
for (var i = 0; i < N; i++) {
var asn = s.copyTo(ss);
var thisSheetDate = new Date(year, month, day+(i+1));
asn.getRange(dateCell).setValue(thisSheetDate);
asn.setName(Utilities.formatDate(thisSheetDate, "GMT-08:00", "MM.dd.yy"));
var protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var p = protections[i];
var rangeNotation = p.getRange().getA1Notation();
var p2 = sheet2.getRange(rangeNotation).protect();
p2.setDescription(p.getDescription());
p2.setWarningOnly(p.isWarningOnly());
if (!p.isWarningOnly()) {
p2.removeEditors(p2.getEditors());
p2.addEditors(p.getEditors());
// p2.setDomainEdit(p.canDomainEdit());
}
}
}
}
Any help would be greatly appreciated.
Also, new to this and if you couldn't tell, kind of a noob. So any references to help grow would be awesome. Thanks!
Issue:
You are using the same variable (i) for two different for loops, one nested inside the other. This is messing up with your dates, causing the error you're getting.
Solution:
Change the variable name of the inner loop (for example, to j):
for (var j = 0; j < protections.length; j++) {
var p = protections[j];
Further issues:
You are setting protections to sheet2, which corresponds to the copied sheet with name 11..20, but not to the rest of sheets (actually, I'm not sure what's the point of making this copy, so I'd just delete the line sheet2 = sheet.copyTo(ss).setName('11..20');). In order to set the protections to each copied sheet, you should use asn instead:
var p2 = asn.getRange(rangeNotation).protect();
Since you want to copy the file named Template.01.20, there is no point in getting the active sheet and storing it in s. I'd just change the mentions of s to sheet (and remove the line var s = ss.getActiveSheet();, since it's not needed):
var startDate = new Date(sheet.getRange(dateCell).getValue());
// ...
var asn = sheet.copyTo(ss);
Since the number of sheets to copy depends on how many days the month has, I'd suggest you to dynamically find that number. You can do that using the following function, for example (credits to Juan Mendes):
function getDaysInMonth(year, month, day) {
var date = new Date(year, month, day);
var days = [];
while (date.getMonth() === month) {
days.push(new Date(date));
date.setDate(date.getDate() + 1);
}
return days;
}
Which you could then call in your main function:
var dates = getDaysInMonth(year, month, day + 1);
for (var i = 0; i < dates.length; i++) {
var asn = sheet.copyTo(ss);
var thisSheetDate = dates[i];
Code sample:
Therefore, your code could be something like this instead:
function duplicateSheetWithProtections() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dateCell = "A2";
var sheet = ss.getSheetByName('Template.01.20');
var startDate = new Date(sheet.getRange(dateCell).getValue());
var day = startDate.getDate();
var month = startDate.getMonth();
var year = startDate.getFullYear();
var dates = getDaysInMonth(year, month, day + 1);
for (var i = 0; i < dates.length; i++) {
var asn = sheet.copyTo(ss);
var thisSheetDate = dates[i];
asn.getRange(dateCell).setValue(thisSheetDate);
asn.setName(Utilities.formatDate(thisSheetDate, "GMT-08:00", "MM.dd.yy"));
var protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var j = 0; j < protections.length; j++) {
var p = protections[j];
var rangeNotation = p.getRange().getA1Notation();
var p2 = asn.getRange(rangeNotation).protect();
p2.setDescription(p.getDescription());
p2.setWarningOnly(p.isWarningOnly());
if (!p.isWarningOnly()) {
p2.removeEditors(p2.getEditors());
p2.addEditors(p.getEditors());
}
}
}
}
function getDaysInMonth(year, month, day) {
var date = new Date(year, month, day);
var days = [];
while (date.getMonth() === month) {
days.push(new Date(date));
date.setDate(date.getDate() + 1);
}
return days;
}
My following code is working fine but the issue is, I want to add a third condition range.getCell(i, 3) value is not an email address, then the corresponding row will be deleted. How to fix it?
var spreadsheet = SpreadsheetApp.getActive();
var dashboard = spreadsheet.getSheetByName("Dashboard");
var sheetName = dashboard.getRange("A4").getValue();
//retrieve the start date to use as desired
var startDate = dashboard.getRange("B4").getDisplayValue();
var endDate = dashboard.getRange("C4").getDisplayValue();
var sheet = spreadsheet.getSheetByName(sheetName);
//chose the range within the specified dates, for this first locate the date column
var startRow = 2;
var dateColumn = sheet.getRange(startRow,1,sheet.getLastRow(), 1);
var dates = dateColumn.getDisplayValues().flat();
var firstRow = dates.indexOf(startDate)+startRow;
var lastRow = dates.lastIndexOf(endDate)+startRow;
//now get the range between (and including) those rows
var range = sheet.getRange(firstRow, 1, lastRow-firstRow+1, sheet.getLastColumn());
var deleteRows = 0;
for (var i = range.getHeight(); i >= 1; i--){
if(range.getCell(i, 1).isBlank() || range.getCell(i, 3).isBlank()
**|| range.getCell(i, 3) IS NOT AN EMAIL Address){**
sheet.deleteRow(range.getCell(i, 1).getRow());
deleteRows++;
}
else{
if(range.getCell(i, 6).isBlank()){
range.getCell(i, 6).setValue(sheetName);
range.getCell(i, 1).setNumberFormat("yyyy-mm-dd");
}
}
}
Since I don't have access to your sheet, it is really difficult for me to undestand if the rest of the code works.
But I managed to replicate part of your example and created the following code snippet which you can use instead of your code after range:
var data = sheet.getRange(firstRow, 1, sheet.getLastRow()-firstRow+1, sheet.getLastColumn()).getValues();
var deleteRows = 0;
for (var i = data.length-1; i >= 1; i--){
if(data[i][0] == '' || data[i][2] == '' || !data[i][2].toString().includes('#')){
sheet.deleteRow(i+firstRow)
deleteRows++;
}
else if(data[i][5] == '') {
sheet.getRange(i+firstRow,6).setValue('sheetName');
sheet.getRange(i+firstRow,1).setNumberFormat("yyyy-mm-dd");
}
}
I have autoincremment in my google scripts. I would like add script with fixed timestamp. And connect with auto_increment in one time.
I started auto_increment script and next I start insert_timestamp
function auto_increment() {
var AUTOINC_COLUMN = 0; //start column
var HEADER_ROW_COUNT = 340;// start row
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var worksheet = spreadsheet.getSheetByName("Sheet 1");
var rows = worksheet.getDataRange().getNumRows() - 1;
var vals = worksheet.getSheetValues(1, 1, rows+1, 2);
for (var row = HEADER_ROW_COUNT; row < vals.length; row++) {
try {
var id = vals[row][AUTOINC_COLUMN];
Logger.log(id);Logger.log((""+id).length ===0);
if ((""+id).length === 0) {
// Here the columns & rows are 1-indexed
worksheet.getRange(row+1, AUTOINC_COLUMN+1).setValue(row);
}
} catch(ex) {
// Keep calm and carry on
}
}
}
function insert_timestamp() { //function onEdit() I cannot because I need fixed date one day
var s = SpreadsheetApp.getActiveSheet();
var r = s.getActiveCell();
var tz = Session.getScriptTimeZone();
if( r.getColumn() != 0 ) {
var row = r.getRow();
var time = new Date();
time = Utilities.formatDate(time, tz, "dd.MM.yyyy");//"dd.MM.yyyy hh:mm:ss");
SpreadsheetApp.getActiveSheet().getRange('C' + row.toString()).setValue(time);
}
}
When I write on cell "E" scripts insert new ID and timestamp "dd.mm.yyyy" and fixed when I change text in cell "E" date fixed the last.
Thank you for your help
I have a Spreadsheet with many sheets inside it, Each sheet represents one month, and has dates in column B. I'm trying to create a function that searches for the current date and inserts the current time in column C. I've got a function to work by specifying the sheet, but I want it to find the sheets itself.
I've got the following code:
function insertTime() {
var date = new Date();
var hours = date.getHours()
var minutes = date.getMinutes()
var timeStamp = hours + ':' + minutes
var ss = SpreadsheetApp.getActiveSpreadsheet()//// get the sheet
var sheets = ss.getSheets();// get all the sheets
var today = new Date(); //get todays date
for (var sheetNum = 1; sheetNum < sheets.length; sheetNum++){
var sheet = ss.getSheets()[sheetNum]
var columnB = sheet.getRange(2, 2, sheet.getLastRow()-1, 1); // get all the rows
var bValues = columnB.getValues(); // get the values
for (var i = 0; i < bValues.length; i++) { // repeat loop
var fDate = new Date(bValues[i][0]);
if (fDate.getDate() == today.getDate() &&
fDate.getMonth() == today.getMonth() &&
fDate.getFullYear() == today.getFullYear()) { // if the date in the cell is today's date...
sheet.getRange(i + 2, 3, 1, 1).setValue(timeStamp);
}
}
}
}
Which is giving me the following error: "The coordinates or dimensions of the range are invalid. (line 16)"
At least one of your sheets is empty or has only 1 row of data.
In this case on line 16, sheet.getLastRow()-1 will be 0 or -1 which is creating the error.
I figured it out in the end, I used the forEach function:
function myFunction(){
var date = new Date();
var hours = date.getHours()
var minutes = date.getMinutes()
var timeStamp = hours + ':' + minutes
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
sheets.forEach (function (sheet) {
var columnB = sheet.getRange(2, 2, 70, 1);
var bValues = columnB.getValues();
var today = new Date();
for (var i = 0; i < bValues.length; i++) {
var fDate = new Date(bValues[i][0]);
if (fDate.getDate() == today.getDate() &&
fDate.getMonth() == today.getMonth() &&
fDate.getFullYear() == today.getFullYear()) {
sheet.getRange(i + 2, 7, 1, 1).setValue(timeStamp);
}
}
}