Google Apps Script to protect range based on dates - google-apps-script

im trying to make a google script to protect a range in google sheets based on dates
I want to protect all the rows that its date is 14 days before today
this is the code I have so far,
function ProtectEntradas() {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const sheetEntradas = ss.getSheetByName('Entradas')
var dateRange = sheetEntradas.getRange(3, 1, sheetEntradas.getLastRow() - 2, 1);
var val = dateRange.getDisplayValues();
var date = new Date();
var protectDateRaw = new Date(date.getFullYear(), date.getMonth(), date.getDate() - 14);
var protectDate = Utilities.formatDate(protectDateRaw, Session.getScriptTimeZone(), "dd-MMM-YY");
var protectRow;
//check if date is less than the current date
for (var i = 0; i < val.length; i++) {
if (val[i][0] >= protectDate) {
protectRow = i;
break;
}
}
var protection = sheetEntradas.getProtections(SpreadsheetApp.ProtectionType.RANGE);
//If protection exists, update else add new one.
if (protection.length > 0) {
var range = sheetEntradas.getRange(3, 1, protectRow, 10);
protection[0].setRange(range);
}
else {
sheetEntradas.getRange(3, 1, protectRow, 10).protect();
}
it is doing some protection, but not the range im expecting
what im I doing wrong ?
then I want to make a trigger to run every day so it will be protecting the range dinamically
Thanks in advance

Try this to compare correctly dates (3 lignes of code has been modified)
function ProtectEntradas() {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const sheetEntradas = ss.getSheetByName('Entradas')
var dateRange = sheetEntradas.getRange(3, 1, sheetEntradas.getLastRow() - 2, 1);
// #### modification on how to fetch dates
var val = dateRange.getValues().map(d => Utilities.formatDate(d[0], Session.getScriptTimeZone(), "yyyy-MM-dd"))
var date = new Date();
var protectDateRaw = new Date(date.getFullYear(), date.getMonth(), date.getDate() - 14);
// #### modification of the format
var protectDate = Utilities.formatDate(protectDateRaw, Session.getScriptTimeZone(), "yyyy-MM-dd");
var protectRow;
//check if date is less than the current date
for (var i = 0; i < val.length; i++) {
// #### modification
if (val[i] >= protectDate) {
protectRow = i;
break;
}
}
var protection = sheetEntradas.getProtections(SpreadsheetApp.ProtectionType.RANGE);
//If protection exists, update else add new one.
if (protection.length > 0) {
var range = sheetEntradas.getRange(3, 1, protectRow, 10);
protection[0].setRange(range);
}
else {
sheetEntradas.getRange(3, 1, protectRow, 10).protect();
}
}
edit : if you want to add another criterion, you will have to protect each row (or group of rows) individually
function ProtectEntradas() {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const sheetEntradas = ss.getSheetByName('Entradas')
// dates
var dateRange = sheetEntradas.getRange(3, 1, sheetEntradas.getLastRow() - 2, 1); // column A
var val = dateRange.getValues().map(d => Utilities.formatDate(d[0], Session.getScriptTimeZone(), "yyyy-MM-dd"))
var date = new Date();
var protectDateRaw = new Date(date.getFullYear(), date.getMonth(), date.getDate() - 14);
var protectDate = Utilities.formatDate(protectDateRaw, Session.getScriptTimeZone(), "yyyy-MM-dd");
// other criteria
var crit = sheetEntradas.getRange(3, 17, sheetEntradas.getLastRow() - 2, 1).getValues().flat(); // column Q
// remove protections
sheetEntradas.getProtections(SpreadsheetApp.ProtectionType.RANGE)
.forEach(protection => {
if (protection && protection.canEdit()) {
protection.remove();
}
});
for (var i = 0; i < val.length; i++) {
if (val[i] <= protectDate && crit[i] != '') {
sheetEntradas.getRange(3 + i, 1, 1, 10).protect();
}
}
}

Related

Moving Row in Google Sheets if Date is Past 60 Days from Today

I am trying to automate moving a row of data from a sheet called Pending to a sheet called Closed if the bid due date (located in column F) is older than 60 days. I am a beginner and use a lot of outside sources to help me build what I want, but I don't fully understand what I am writing, as long as it works.
This is what I have tried to write and I am looking for some fine tuning
function approveRequests() {
// Initialising
var ss = SpreadsheetApp.getActiveSpreadsheet();
var scheduleSheet = ss.getSheetByName("Pending");
var pastSheet = ss.getSheetByName("Closed");
var lastColumn = scheduleSheet.getLastColumn();
for(var i = scheduleSheet.getLastRow(); i > 60; i--){
var dateCell = scheduleSheet.getRange(i, 1).getValue();
if(isValidDate(dateCell)){
var today = new Date();
var test = new Date(dateCell);
if(test < today){
var rangeToMove = scheduleSheet.getRange(i, 1, 1, scheduleSheet.getLastColumn()).getValues();
pastSheet.getRange(pastSheet.getLastRow() + 1, 1, 1, scheduleSheet.getLastColumn()).setValues(rangeToMove);
scheduleSheet.deleteRow(i);
}
}
}
}
function isValidDate(value) {
var dateWrapper = new Date(value);
return !isNaN(dateWrapper.getDate());
}
Move Row is column 1 is older than 60 days:
function approveRequests() {
const ss = SpreadsheetApp.getActive();
const sh1 = ss.getSheetByName("Pending");
const vs1 = sh1.getRange(61,1,sh1.getLastRow() - 60, sh1.getLastColumn()).getValues();
const sh2 = ss.getSheetByName("Closed");
const dt = new Date();
const dtv = new Date(dt.getFullYear(),dt.getMonth(),dt.getDate() - 60);
let d = 0;
vs1.forEach((r,i) => {
if (new Date(r[0]).valueOf() < dtv) {
sh2.getRange(sh2.getLastRow() + 1, 1, 1, r.length).setValues([r]);
sh1.deleteRow(i + 61 - d++);
}
});
}

How can I set one onOpen function for several sheets?

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.

Simplifying an Archiving script

I have a script entered below which I am trying to use on a daily worksheet with multiple pages, I have an archive sheet where the information is meant to be copied across at end of business. Both sets of sheets are named identical to ease back up. However I get a lot of blank lines with the date entered and not the other information. I have a Menu UI to add a button for the staff to do a back up at end of day as well, again to make life easier. I do also have a few other scripts attached to the file so not sure if (most likely the "Font and Alignment" script is causing an issue.
Can anyone see what I am doing wrong?
function menu() { // This function adds a custom menu to the spreadsheet (Backup to archive) so you can run the script from there.
var ui = SpreadsheetApp.getUi();
ui.createMenu('Backup to archive')
.addItem('Backup', 'dataBackup')
.addToUi();
}
function dataBackup() {
var check = alert("Data Backup","Are you sure you want to backup today's entries?");
if(!check){ return; }
var inputSS = SpreadsheetApp.getActiveSpreadsheet();
var user = Session.getActiveUser().getEmail();
var sheetNames = ['AM trip', 'PM trip', 'Pool / Beach', 'Night Dive'];
for (var i = 0; i < sheetNames.length; i++) {
var inputSheet = inputSS.getSheetByName(sheetNames[i]);
var archiveSheet = SpreadsheetApp.openById('146WU8RghfFqlCpCSX7n6kBAKOyxcpVKt14yhVfvYz-g');
var date = inputSheet.getRange('A1').getValue(); // Gets todays date from cell A1
var dataLen = inputSheet.getRange('E7:A37').getValues().filter(String); // Gets the number of entries made today
var dataRange = inputSheet.getRange('E7:U37'); // Gets the range of cells A7:U37
var data = dataRange.getValues().slice(0, dataLen.length); // Removes any rows that don't have a number in the '#' column
for (var x = 0; x < data.length; x++) { // Adds todays date to the start of each row.
var temp = data[x].splice(0, 0, date)
}
var getDate = archiveSheet.getRange(archiveSheet.getLastRow(), 1).getValue();
if (getDate != date) { // Checks for duplicate backup
if (data.length != 0) { // If there are entries with todays date
archiveSheet.insertRowsAfter(archiveSheet.getLastRow(), data.length); // Inserts the required amount of rows
archiveSheet.getRange(archiveSheet.getLastRow() + 1, 1, data.length, data[0].length).setValues(data).setNumberFormat("#"); // inserts the data to the archive sheet
} else {
archiveSheet.insertRowsAfter(archiveSheet.getLastRow(), 1); // If there was no data, inserts 1 row
archiveSheet.getRange(archiveSheet.getLastRow() + 1, 1, 1, 2).setValues([[date, 'No Data']]); // Inserts todays date and 'No Data'
}
}
}
}
function alert(title, message)
var ui = SpreadsheetApp.getUi();
var alert = ui.alert(title, message, ui.ButtonSet.YES_NO);
var response;
if(alert == "YES"){
response = true
} else {
response = false
}
return response;
}
The link below is for the archive sheet:-
https://docs.google.com/spreadsheets/d/146WU8RghfFqlCpCSX7n6kBAKOyxcpVKt14yhVfvYz-g/edit?usp=sharing
Below is a sample of an input sheet:-
https://docs.google.com/spreadsheets/d/1XwPFgVP_DlsBp4Th8pR7qKvx4Bh1zUubHabgMdDD3ck/edit?usp=sharing
Thank you.
Below is the now working backup script. I have an odd issue though that the menu button isn't appearing until I call it manually from the script editor? Each time I am asked to review permissions but never had that issue before, is it because of another running script? Maybe the font alignment one?
}
function dataBackup() {
var inputSS = SpreadsheetApp.getActiveSpreadsheet();
var archiveSS = SpreadsheetApp.openById('146WU8RghfFqlCpCSX7n6kBAKOyxcpVKt14yhVfvYz-g');
var user = Session.getActiveUser().getEmail();
var sheetNames = ['AM trip', 'PM trip', 'Pool / Beach', 'Night Dive'];
for (var i = 0; i < sheetNames.length; i++) {
var inputSheet = inputSS.getSheetByName(sheetNames[i]);
var archiveSheet = archiveSS.getSheetByName(sheetNames[i]);
var date = inputSheet.getRange('A1').getValue();
var data = inputSheet.getRange('E7:U37').getValues().filter(function(row) { return row[0] !== '' || row[1] !== ''});
for (var x = 0; x < data.length; x++) {
data[x].splice(0, 0, date);
}
var getDate = archiveSheet.getRange(archiveSheet.getLastRow(), 1).getValue();
var maxRowLength = data.reduce(function(length, row) { return Math.max(length, row.length); }, 0);
var date = new Date(date);
var getDate = new Date(getDate);
if (getDate.getDate() != date.getDate() || getDate.getMonth() != date.getMonth()) {
if (data.length != 0) {
archiveSheet.insertRowsAfter(archiveSheet.getLastRow(), data.length);
archiveSheet.getRange(archiveSheet.getLastRow() + 1, 1, data.length, maxRowLength).setValues(data);
} else {
archiveSheet.insertRowsAfter(archiveSheet.getLastRow(), 1);
archiveSheet.getRange(archiveSheet.getLastRow() + 1, 1, 1, 2).setValues([[date, 'No Data']]);
}
}
}
#SpiderPig OK, it was working and now I am not sure what the issue is with the script below. I have adjusted worksheet name cell to A2 to avoid inadvertent removal as A1 is the active cell when opening and have had instances where stuff gets typed there. But now in the archive sheet I get the date 01/01/1970 even though A2 has 01.01.18. The no data is correct. I have tried changing column to Date format and made sure input sheet is Date formatted. I also have to authorise the script each time but don't want to do that if I can help it. Can I run the script on a time trigger say at 2AM, would this still need authorising for each copy I make. Want to make a copy for each day of the year with the correct day / date name.
function onOpen() { // This function adds a custom menu to the spreadsheet (Backup to archive) so you can run the script from there.
var ui = SpreadsheetApp.getUi();
ui.createMenu('Backup')
.addItem('Backup','dataBackup')
.addToUi();
}
function dataBackup() {
var inputSS = SpreadsheetApp.getActiveSpreadsheet();
var archiveSS =
SpreadsheetApp.openById('146WU8RghfFqlCpCSX7n6kBAKOyxcpVKt14yhVfvYz-g');
var user = Session.getActiveUser().getEmail();
var sheetNames = ['AM trip', 'PM trip', 'Pool / Beach', 'Night Dive'];
for (var i = 0; i < sheetNames.length; i++) {
var inputSheet = inputSS.getSheetByName(sheetNames[i]);
var archiveSheet = archiveSS.getSheetByName(sheetNames[i]);
var date = inputSheet.getRange('A2').getValue(); // Changed to stop
inadvertent cell changes, also made text white so not seen.
var data =
inputSheet.getRange('E7:U37').getValues().filter(function(row) { return
row[0] !== '' || row[1] !== ''});
for (var x = 0; x < data.length; x++) {
data[x].splice(0, 0, date);
}
var getDate = archiveSheet.getRange(archiveSheet.getLastRow(),
1).getValue();
var maxRowLength = data.reduce(function(length, row) { return
Math.max(length, row.length); }, 0);
var date = new Date(date);
var getDate = new Date(getDate);
if (getDate.getDate() != date.getDate() || getDate.getMonth() !=
date.getMonth()) {
if (data.length != 0) {
archiveSheet.insertRowsAfter(archiveSheet.getLastRow(),
data.length);
archiveSheet.getRange(archiveSheet.getLastRow() + 1, 1,
data.length, maxRowLength).setValues(data);
} else {
archiveSheet.insertRowsAfter(archiveSheet.getLastRow(), 1);
archiveSheet.getRange(archiveSheet.getLastRow() + 1, 1, 1,
2).setValues([[date, 'No Data']]);
}
}
}
}
I tried to improve the script a bit. I also changed the way dates are compared since I felt the old way may not work reliably.
function dataBackup() {
var inputSS = SpreadsheetApp.getActiveSpreadsheet();
var archiveSS = SpreadsheetApp.openById('146WU8RghfFqlCpCSX7n6kBAKOyxcpVKt14yhVfvYz-g');
var user = Session.getActiveUser().getEmail();
var sheetNames = ['AM trip', 'PM trip', 'Pool / Beach', 'Night Dive'];
for (var i = 0; i < sheetNames.length; i++) {
var inputSheet = inputSS.getSheetByName(sheetNames[i]);
var archiveSheet = archiveSS.getSheetByName(sheetNames[i]);
var date = inputSheet.getRange('A1').getValue();
var data = inputSheet.getRange('E7:U37').getValues().filter(function(row) { return row[0] !== '' || row[1] !== ''});
for (var x = 0; x < data.length; x++) {
data[x].splice(0, 0, date);
}
var getDate = archiveSheet.getRange(archiveSheet.getLastRow(), 1).getValue();
var maxRowLength = data.reduce(function(length, row) { return Math.max(length, row.length); }, 0);
if (getDate.getDate() != date.getDate() || getDate.getMonth() != date.getMonth()) {
if (data.length != 0) {
archiveSheet.insertRowsAfter(archiveSheet.getLastRow(), data.length);
archiveSheet.getRange(archiveSheet.getLastRow() + 1, 1, data.length, maxRowLength).setValues(data);
} else {
archiveSheet.insertRowsAfter(archiveSheet.getLastRow(), 1);
archiveSheet.getRange(archiveSheet.getLastRow() + 1, 1, 1, 2).setValues([[date, 'No Data']]);
}
}
}
}

Google App Script to find a date across multiple Sheets

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);
}
}
}

Trying to delete rows in a spreadsheet based on the date

I'm trying to loop through the rows of my sheet to delete certain rows based on the date found in column F. I have an syntax error showing up on line 28. I'm not sure what's triggering this as the code looks fine to me. Any advice would be appreciated.
function dateReduce() {
var ss = SpreadsheetApp.openById('1yl599Ff7d1aaSTKpHAeHW5wswiDOzNegrKxS1Z1SquY');
var sheet = ss.getSheetByName("worksheet");
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth()+1;
var yyyy = today.getFullYear();
var range = sheet.getRange(2, 6, 1374);
var values = range.getValues(); // getValues() results in a javascript array as opposed to a google script array
var numRows = range.getNumRows();
var endDate;
for (var i = 1; i <= numRows; i++) {
endDate = new Date(values[i][5]);
if (yyyy > endDate.getFullYear()) {
sheet.deleteRow(i);
};
else if (yyyy === endDate.getFullYear()) {
if ( mm > (endDate.getMonth()+1)) {
sheet.deleteRow(i);
};
};
else if ((mm === endDate.getMonth()+1) && (yyyy == endDate.getFullYear())) {
if (dd > endDate.getDate()) {
sheet.deleteRow(i);
};
};
};
};
You need to remove the semicolons after all of the ending curly braces ("}"). You don't need to put semicolons after curly braces unless there is an assignment operation involved.
function dateReduce() {
var ss = SpreadsheetApp.openById('1yl599Ff7d1aaSTKpHAeHW5wswiDOzNegrKxS1Z1SquY');
var sheet = ss.getSheetByName("worksheet");
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth() + 1;
var yyyy = today.getFullYear();
var range = sheet.getRange(2, 6, 1374);
var values = range.getValues(); // getValues() results in a javascript array as opposed to a google script array
var numRows = range.getNumRows();
var endDate;
for (var i = 1; i <= numRows; i++) {
endDate = new Date(values[i][5]);
if (yyyy > endDate.getFullYear()) {
sheet.deleteRow(i);
} else if (yyyy === endDate.getFullYear()) {
if (mm > (endDate.getMonth() + 1)) {
sheet.deleteRow(i);
}
} else if ((mm === endDate.getMonth() + 1) && (yyyy == endDate.getFullYear())) {
if (dd > endDate.getDate()) {
sheet.deleteRow(i);
}
}
}
}