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.
Related
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.
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());
}
I have been working on a google sheet that that receives a form submission.
Once the submission comes in I need a script to move the rows of data to different tabs depending on the name shown in column C.
I have extensively searched for solutions on stack overflow and I am very close to a solution right now through the code I have found and edited.
I have a script that will move historic dates to a different tab and leave all future dates in the original tab based in the logic of the date being older than today.
All I need to do now is modify this to move the rows with the name "John" in column C to the John tab and ignore the date.
Once I can get one name to work I am confident I can make this work for multiple names and multiple tabs.
Please feel free to create a copy of the following test sheet I have been working on.
link:
https://docs.google.com/spreadsheets/d/1zJpylrD_5hzScW3lIjIQQSKiY0Aan6Wkm_h_IbVrVXM/edit#gid=0
function MovePastDates() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var entrySheet = ss.getSheetByName("Entry Sheet");
var franksSheet = ss.getSheetByName("Franks Sheet");
var lastColumn = entrySheet.getLastColumn();
for(var i = entrySheet.getLastRow(); i > 0; i--){
var dateCell = entrySheet.getRange(i, 1).getValue();
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 = entrySheet.getRange(i, 1, 1, entrySheet.getLastColumn()).getValues();
franksSheet.getRange(franksSheet.getLastRow() + 1, 1, 1, entrySheet.getLastColumn()).setValues(rangeToMove);
entrySheet.deleteRow(i);
}
}
}
The final result should be a google sheet that receives form entries.
Each entry will be allocated to a specific person who will only have edit permissions to there own tab only where they can approve/decline requests submitted through the form.
All other users of the sheet will have view only access.
I wanted two scripts:
1) Script to move form submission rows to a specific tab dependent on person’s name
(I was going to set up a trigger every minute for this)
2) Script to move past dates into an historic sheet
(I was going to set up a trigger every night for this)
I have been able to modify your code to achieve the desired function, it may not be the most efficient but it appears to work well.
Script One is:
function moveRowsToNamesSheets() { //Name of function
var sObj={John:'Johns Sheet',Frank:'Franks Sheet',David:'Davids Sheet'}; // Put key work and sheet name here in format eg.( keyWord1: 'sheet name to move keyWord1 to')
var ss=SpreadsheetApp.getActive(); // ??
var esh=ss.getSheetByName('Entry Sheet'); //Sheet data is being pulled form
var fsh=ss.getSheetByName('Franks Sheet'); //unsure why one of the sheets is named here
var erg=esh.getDataRange(); // Not sure of function now that I am not using dates
var evA=erg.getValues(); // ??
var d=0; //??
//var today=new Date(new Date().getFullYear(),new Date().getMonth(),new Date().getDate()).valueOf(); // Didnt need this line
for(var i=1;i<evA.length;i++) { //??
if(/*new Date(evA[i][0]).valueOf() < today*/ evA[i][2]=='John' ||evA[i][2]=='Frank' ||evA[i][2]=='David') { //Keywords used go here, what does the [2] mean?
ss.getSheetByName(sObj[evA[i][2]]).appendRow(evA[i]); //??
esh.deleteRow(i+1-d);
d++; //increments d by one
}
}
}
Script Two is:
function HistoricDates() {
// Initialising
var ss = SpreadsheetApp.getActiveSpreadsheet();
//--------------- Franks Sheets --------------------
var franksSheet = ss.getSheetByName("Franks Sheet");
var PastSheet = ss.getSheetByName("Historic Requests");
var lastColumn = franksSheet.getLastColumn();
// Check all values from your "Franks Sheet" sheet
for(var i = franksSheet.getLastRow(); i > 0; i--){
// Check if the value is a valid date
var dateCell = franksSheet.getRange(i, 4).getValue(); //Dates in column 4
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 = franksSheet.getRange(i, 1, 1, franksSheet.getLastColumn()).getValues();
PastSheet.getRange(PastSheet.getLastRow() + 1, 1, 1, franksSheet.getLastColumn()).setValues(rangeToMove);
franksSheet.deleteRow(i);
}
}
}
//---------------------- Johns Sheets -------------------------
var johnsSheet = ss.getSheetByName("Johns Sheet");
var pastSheet = ss.getSheetByName("Historic Requests");
var lastColumn = johnsSheet.getLastColumn();
// Check all values from your "Johns Sheet" sheet
for(var i = johnsSheet.getLastRow(); i > 0; i--){
// Check if the value is a valid date
var dateCell = johnsSheet.getRange(i, 4).getValue(); //Dates in column 4
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 = johnsSheet.getRange(i, 1, 1, johnsSheet.getLastColumn()).getValues();
pastSheet.getRange(pastSheet.getLastRow() + 1, 1, 1, johnsSheet.getLastColumn()).setValues(rangeToMove);
johnsSheet.deleteRow(i);
}
}
}
//--------------- Davids Sheets --------------------
var davidsSheet = ss.getSheetByName("Davids Sheet");
var pastSheet = ss.getSheetByName("Historic Requests");
var lastColumn = davidsSheet.getLastColumn();
// Check all values from your "Davids Sheet" sheet
for(var i = davidsSheet.getLastRow(); i > 0; i--){
// Check if the value is a valid date
var dateCell = davidsSheet.getRange(i, 4).getValue();//Dates in column 4
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 = davidsSheet.getRange(i, 1, 1, davidsSheet.getLastColumn()).getValues();
pastSheet.getRange(pastSheet.getLastRow() + 1, 1, 1, davidsSheet.getLastColumn()).setValues(rangeToMove);
davidsSheet.deleteRow(i);
}
}
}
}
// Check is a valid date
function isValidDate(value) {
var dateWrapper = new Date(value);
return !isNaN(dateWrapper.getDate());
}
The working spreadsheet is located here:
https://docs.google.com/spreadsheets/d/1VCONRkBpkva-KrFDO2bFV8ZTp1U168QWAGavcKCa_uQ/edit?usp=sharing
I think this is what you want:
function movePastDatesOrJohn() {
var sObj={John:'Johns Sheet',Frank:'Franks Sheet',David:'Davids Sheet'};
var ss=SpreadsheetApp.getActive();
var esh=ss.getSheetByName('Entry Sheet');
var fsh=ss.getSheetByName('Franks Sheet');
var erg=esh.getDataRange();
var evA=erg.getValues();
var d=0;
var today=new Date(new Date().getFullYear(),new Date().getMonth(),new Date().getDate()).valueOf();
for(var i=1;i<evA.length;i++) {
if(new Date(evA[i][0]).valueOf() < today || evA[i][2]=='John') {
ss.getSheetByName(sObj[evA[i][2]]).appendRow(evA[i]);
esh.deleteRow(i+1-d);
d++;
}
}
}
So Franks Sheet and Davids Sheet only get the rows that are older than today. But Johns Sheet gets all of the row that are Johns and disregards the date. I think that's what you wanted.
By the way, did you know that if you more that one form attached to your spreadsheet you can tell which response sheet the formSubmit trigger is writing too, with the event object range? Using sheet name = e.range.getSheet().getName();
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);
}
}
}
I want a Google Script which checks once a day on my spreadsheet and copies the values to another sheet and than deletes all the rows if the date in that cell is small than the current.
The file looks like this:
And this is what I wrote so far:
function DeleteIfDateIsToSmall(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
var today = new Date();
var today2 = Utilities.formatDate(today,'GMT+0200','dd.MM.yyyy');
var startRow = 2; // First row of data to process
var numRows = sheet.getLastRow()-1
for (var i=2; i < numRows; i++) {
var DateCell = s.getRange(i, 13);
var sheetDate = DateCell.getValue()
var sheetDate2 = Utilities.formatDate(sheetDate,'GMT+0200','dd.MM.yyyy');
var row = i;
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Ended or Deleted");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
if (s.getName() == "Start" && sheetDate != 0 && sheetDate != "" && today2.valueOf() > sheetDate2.valueOf()){
s.getRange(row, 1, 1, numColumns).moveTo(target);
s.deleteRow(row);
}
}
}
It doesn't seem to work, please help.
A few things are wrong or suboptimal here.
For a timed trigger, the event object does not have any range/source data; it only has time data. You need to pick a sheet by name (or loop through all sheets obtained by getSheets, if this is what you want).
Utilities.formatDate returns a string, which is not the best way to compare dates; in any case calling valueOf on it seems pointless.
Instead of using getValue in a loop, it is more efficient to obtain values before entering the loop, with getValues. There are some other things you do in the loop that should be outside.
Deleting rows when moving top to bottom in a sheet is tricky, because rows shift, messing up their indexing. For this reason, I collect the rows to be deleted in an array, and then delete them in bottom-to-top order.
function DeleteIfDateIsTooSmall() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName("Start");
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Ended or Deleted");
var today = new Date();
var dates = s.getRange(2, 13, s.getLastRow()-1, 1).getValues();
var rowsToDelete = [];
for (var i = 0; i < dates.length; i++) {
if (dates[i][0] && dates[i][0] < today) {
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(i + 2, 1, 1, numColumns).moveTo(target);
rowsToDelete.push(i + 2);
}
}
for (i = rowsToDelete.length - 1; i >= 0; i--) {
s.deleteRow(rowsToDelete[i]);
}
}