复制 Sheet 带保护并将新副本的名称更改为日期

Copy Sheet with protections and change new Duplicate's name to a date

我有一个用于组数据输入的模板 sheet。 sheet 的大部分内容都是免费的,但有些标题行我不想编辑,所以我对它们进行了保护。每个月的每一天都有一个这样的标签,每个月都有一个新的 Sheet。

我想根据月份将模板复制 30-31 次,并将 sheet 的标题设为相应的日期(MM.dd.yy 即:11.02.20)。我在 A2 中设置了日期(即:11/01/2020)。

到目前为止,我尝试结合保护和日期更改,但我不断收到可变错误,有时它会双倍创建 sheets(例如 11.06.20 然后停止)。

这是我试过的代码(并编辑和移动了几次)。

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

如有任何帮助,我们将不胜感激。 另外,这是新手,如果您不知道,那就是菜鸟。因此,任何有助于成长的参考资料都会很棒。谢谢!

问题:

您正在为两个不同的 for 循环使用相同的变量 (i),一个嵌套在另一个循环中。这会打乱您的日期,导致您收到错误。

解决方案:

更改内循环的变量名(例如,改为j):

  for (var j = 0; j < protections.length; j++) {
    var p = protections[j];

其他问题:

  • 您正在为 sheet2 设置保护,它对应于名称为 11..20 的复制 sheet,但不对应 sheet 的其余部分(实际上,我我不确定制作此副本的意义何在,所以我只是删除了行 sheet2 = sheet.copyTo(ss).setName('11..20');)。为了对每个复制的 sheet 设置保护,您应该使用 asn 代替:
var p2 = asn.getRange(rangeNotation).protect();
  • 由于您要复制名为 Template.01.20 的文件,因此没有必要获取活动 sheet 并将其存储在 s 中。我只是将 s 的提及更改为 sheet(并删除行 var s = ss.getActiveSheet();,因为它不需要):
var startDate = new Date(sheet.getRange(dateCell).getValue());
// ...
var asn = sheet.copyTo(ss);
  • 由于要复制的 sheet 的数量取决于该月的天数,我建议您动态查找该数字。您可以使用以下函数来做到这一点,例如(归功于 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;
}

然后您可以在主函数中调用:

  var dates = getDaysInMonth(year, month, day + 1);
  for (var i = 0; i < dates.length; i++) {
    var asn = sheet.copyTo(ss);
    var thisSheetDate = dates[i];

代码示例:

因此,您的代码可能是这样的:

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