0

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!

aynber
  • 22,380
  • 8
  • 50
  • 63
J M
  • 21
  • 3
  • If I understand you correctly, you want to (1) create a new spreadsheet with a sheet for each day of the month from the date in A2 from another sheet, and (2) make each sheet have the same protections as the original template. Is that correct? Also, could you please clarify what errors you are getting, and where? From what I can see, you didn't add `var` when first assigning values to `sheet` and `sheet2`, were these variables declared outside of the function? – Iamblichus Nov 13 '20 at 09:00
  • Heya! Pretty much yes. 1) I want to duplicate the form "Template.01.20" starting from the date in A2 which would be first of the month through the end of the month (n=30). 2) Yes same protections as the Template sheet. Using this it will create a sheet titled "11..20" and then "11.02.20" and then "11.06.20" then "Copy of Template.01.20" and then "Copy of Template.01.20" and then I will get an error that a page with the name "11.06.20" already exists. – J M Nov 13 '20 at 22:56
  • Hi, I posted a answer regarding this. The main problem was the counter variable used in both for loops. Let me know if that works for you. – Iamblichus Nov 16 '20 at 13:56

1 Answers1

0

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;
}
Iamblichus
  • 18,540
  • 2
  • 11
  • 27
  • Thanks so much for this. I tried to run these functions and nothing occurs. It doesn't give me any error messages, just no responses on the sheet. – J M Nov 16 '20 at 15:47
  • @JM I tested it before posting, it works. Have you copied the code snippet exactly as I posted it? Can you provide a copy of the spreadsheet you are working on, so that I can troubleshoot this? – Iamblichus Nov 16 '20 at 15:50
  • 1
    I just closed down Chrome and reopened it and it ran beautifully!! Thanks so much! – J M Nov 16 '20 at 16:31