Google App Script to find a date across multiple Sheets - google-apps-script

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

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

Google Apps Script to protect range based on dates

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

Function to protects rows doesn't work for multiple sheets

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.

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.

Move Rows From Multiple Tabs With a Historic Date in Column A to a Single Tab

I have a spreadsheet with multiple tabs all with a date in column A.
I need a script that I can run using a trigger each night to move all rows with a historic date in Column A to a single tab for historic rows.
I have been successful in getting historic rows in my first sheet to move to the historic tab however I cannot seem to make the script work for multiple tabs.
function HistoricDates() {
SHEET_NAME = "Area1" || "Area2" || "Area3"||"Area4";
// Initialising
var ss = SpreadsheetApp.getActiveSpreadsheet();
var Sheet = ss.getSheetByName(SHEET_NAME);
var PastSheet = ss.getSheetByName("Historic Sheet");
var lastColumn = Sheet.getLastColumn();
// Check all values from sheets
for(var i = Sheet.getLastRow(); i > 0; i--){
// Check if the value is a valid date
var dateCell = Sheet.getRange(i, 1).getValue(); //Dates in column 1
if(isValidDate(dateCell)){
var today = new Date();
var test = new Date(dateCell);
// If the value is a valid date and is a past date, we remove it from the sheet to paste on the other sheet
if(test < today){
var rangeToMove = Sheet.getRange(i, 1, 1, Sheet.getLastColumn()).getValues();
PastSheet.getRange(PastSheet.getLastRow() + 1, 1, 1, Sheet.getLastColumn()).setValues(rangeToMove);
Sheet.deleteRow(i);
}
}
}}
// Check is a valid date
function isValidDate(value) {
var dateWrapper = new Date(value);
return !isNaN(dateWrapper.getDate());
}
The expected result would be for all historic rows in Area 2,3 & 4 to move to move to the single historic tab.
My spreadsheet with script is available on the following link:
https://docs.google.com/spreadsheets/d/1WiZWok4onddTErdAxlWmU82KRSGfVJr5wi1p-rlbY5E/edit?usp=sharing
The way you defined SHEET_NAME, it will always be "Area 1". You can test this.
function test() {
SHEET_NAME = "Area1" || "Area2" || "Area3"||"Area4";
Logger.log(SHEET_NAME);
}
Instead, SHEET_NAME should be an array, and then you need to loop through that array. Below I've included a sample for how to define that array and get the sheets.
function loopThroughSpecificSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetNames = ["Area1", "Area2", "Area3", "Area4"];
for (var j in sheetNames) {
var sheet = ss.getSheetByName(sheetNames[j]);
// Do other stuff
}
}
Assuming that the rest of your code works correctly (I haven't analyzed it), your modified script would look like this:
function HistoricDates() {
// Initialising
var ss = SpreadsheetApp.getActiveSpreadsheet();
var pastSheet = ss.getSheetByName("Historic Sheet");
var sheetNames = ["Area1", "Area2", "Area3", "Area4"];
for (var j in sheetNames) {
var sheet = ss.getSheetByName(sheetNames[j]);
var lastColumn = sheet.getLastColumn();
// Check all values from sheets
for(var i = sheet.getLastRow(); i > 0; i--) {
// Check if the value is a valid date
var dateCell = sheet.getRange(i, 1).getValue(); //Dates in column 1
if(isValidDate(dateCell)){
var today = new Date();
var test = new Date(dateCell);
// If the value is a valid date and is a past date, we remove it from the sheet to paste on the other sheet
if(test < today) {
var rangeToMove = sheet.getRange(i, 1, 1, sheet.getLastColumn()).getValues();
pastSheet.getRange(pastSheet.getLastRow() + 1, 1, 1, sheet.getLastColumn()).setValues(rangeToMove);
sheet.deleteRow(i);
}
}
}
}
}
// Check is a valid date
function isValidDate(value) {
var dateWrapper = new Date(value);
return !isNaN(dateWrapper.getDate());
}