google-sheetsgoogle-apps-script

How do I configure Apps Script time-based Triggers to execute successfully?


I have implemented 2 Triggers in Apps script. The goal of these triggers are to execute only on 15th and last day of each month. Current issue, triggers are attempting to execute multiple times daily which is causing error exception: This script has too many triggers. Triggers must be deleted from the script before more can be added.

Trigger 1 (copyDatabase): Expected results: Trigger copies a specified range from Source sheet (Index) to Destination sheet (All Work Orders) on the 15th of each month and last day of each month (between 11pm to midnight)

/// TRigger

ScriptApp.newTrigger("copyDatabase")
   .timeBased()
   .atHour(23)
   .everyDays(1) 
   .create();

function copyDatabase() {

  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName("Index");
  var desinationsheet = ss.getSheetByName("All Work Orders");

  var startRow = 2; 
  var numRows = sheet.getLastRow() - startRow + 1; 
  var startCol = 23;
  var numCols = 14;
  var startColTarget = 1;
  var dataRange = sheet.getRange(startRow, startCol, numRows, numCols); 
  var data = dataRange.getValues(); 
  var Copy = "Copy"; 
  var firstEmptyRow = sheet.getRange("A2:P").getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow() + 1;
  var dataToCopy = data.filter(row => row[2] !== Copy);

  desinationsheet.getRange(firstEmptyRow, startColTarget, dataToCopy.length, dataToCopy[0].length).setValues(dataToCopy);
}

Trigger 2 (exportTriggerFunction): Expected results: Trigger executes function and exports to specified sheet/tabs to Excel on the 15th (between 11pm to midnight) and last day of each month (between 11pm to midnight)

///////////////
/// TRigger

ScriptApp.newTrigger("exportTriggerFunction")
   .timeBased()
   .atHour(23)
   .everyDays(1) 
   .create();

function exportTriggerFunction()
{
  var today = new Date();
  var lastDayOfMonth = new Date(today.getFullYear(), today.getMonth()+1, 0);

  if(today.getDate() == lastDayOfMonth.getDate() )
  {
var exportSheetIds = 
  ["560568812","1440952465","439649389","513481136",
   "1088500659","133657514","1618608630","802444630",
   "1834450484","657423004","682313931","1980296394","635144452"]; // Please set the sheet IDs you want to export.

  const ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  var name = sheet.getRange('Reference!M2').getDisplayValue();
  var destination = DriveApp.getFolderById("1HRchNqQ5_0LYzfULw1hWN_ALNuijsy2q"); // Google Drive Folder Location

  // 1. Copy the active Spreadsheet as a tempora Spreadsheet.
  const spreadsheet = SpreadsheetApp.getActiveSpreadsheet().copy('tmp');
  const sheets = spreadsheet.getSheets();
  const check = sheets.filter(s => exportSheetIds.includes(s.getSheetId().toString()));
  if (check.length == 0) {
    throw new Error("No export sheets.");
  }

  // 2. Convert the formulas to the texts.
  sheets.forEach(sheet => {
    if (exportSheetIds.includes(sheet.getSheetId().toString())) {
      const targetRange = sheet.getDataRange();
      targetRange.copyTo(targetRange, { contentsOnly: true });
    }
  });

  // 3. Delete/Exclude sheets: Index and Reference
  sheets.forEach(sheet => {
    if (!exportSheetIds.includes(sheet.getSheetId().toString())) {
      spreadsheet.deleteSheet(sheet);
    }
  });
  SpreadsheetApp.flush();

  // 4. Retrieve the blob from the export URL.
  const id = spreadsheet.getId();
  const xlsxBlob = UrlFetchApp.fetch(`https://docs.google.com/spreadsheets/export?id=${id}&exportFormat=xlsx`, { headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` } }).getBlob();

  // 5. Create the blob as a file.
  destination.createFile(xlsxBlob.setName(`${name}.xlsx`));

  // 6. Delete the temporate Spreadsheet.
  DriveApp.getFileById(id).setTrashed(true);

  }
}

enter image description hereenter image description hereenter image description here

Initially, my strategy for executing on the 15th was to configure each trigger's settings

Select type of time based trigger: Month Timer Select day of month: 15th

And for executing on the last day of the month, I attempted to edit the code script, but this combination is currently causing the error exception.


Solution

  • I believe your goal is as follows.

    In this case, how about the following modification?

    Modification points:

    In order to achieve your goal, how about the following flow?

    1. Run a script for installing the time-driven trigger.
      • In this case, the 15th day or the last day of this month is selected from today's date and time.
    2. When the time-driven trigger is run, the functions copyDatabase and exportTriggerFunction are run. And, the next time-driven trigger is installed.
      • In this case, the 15th day or the last day of this month is selected from today's date and time.
      • Here, the functions copyDatabase and exportTriggerFunction are used by putting into a function like wrapFunction. From your showing script, I guessed that both functions might be able to be finished within 6 minutes.

    When this flow is reflected in your script, it becomes as follows.

    Modified script:

    Please copy and paste the following script to the script editor. And, please manually run the function installTriggers. By this, the 1st time-driven trigger is installed. After this, the functions copyDatabase and exportTriggerFunction are automatically run by automatically installed the time-driven trigger.

    In this modification, only one time-driven trigger is used.

    // --- I added the below script.
    function installTriggers() {
      // These values are from your question.
      const time = 23;
      const dates = [15, 0];
    
    
      const extraTime = 60; // seconds
      const functionName = "wrapFunction";
      ScriptApp.getProjectTriggers().forEach(t => {
        if (t.getHandlerFunction() == functionName) {
          ScriptApp.deleteTrigger(t);
        }
      });
      const now = Date.now();
      const triggerDate = dates.map(e => {
        const d = new Date();
        d.setMonth(d.getMonth() + (e > 0 ? 0 : 1), e);
        d.setHours(time, 0, 0);
        if (d.getTime() < now + (extraTime * 1000)) {
          d.setMonth(d.getMonth() + 1);
        }
        return d;
      }).sort((a, b) => a.getTime() > b.getTime() ? 1 : -1)[0];
      ScriptApp.newTrigger(functionName).timeBased().at(triggerDate).create();
    }
    
    function wrapFunction() {
      installTriggers();
      copyDatabase();
      SpreadsheetApp.flush();
      exportTriggerFunction();
    }
    // ---
    
    
    
    // --- Below script is your showing script.
    function copyDatabase() {
    
      var ss = SpreadsheetApp.getActiveSpreadsheet();
      var sheet = ss.getSheetByName("Index");
      var desinationsheet = ss.getSheetByName("All Work Orders");
    
      var startRow = 2; 
      var numRows = sheet.getLastRow() - startRow + 1; 
      var startCol = 23;
      var numCols = 14;
      var startColTarget = 1;
      var dataRange = sheet.getRange(startRow, startCol, numRows, numCols); 
      var data = dataRange.getValues(); 
      var Copy = "Copy"; 
      var firstEmptyRow = sheet.getRange("A2:P").getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow() + 1;
      var dataToCopy = data.filter(row => row[2] !== Copy);
    
      desinationsheet.getRange(firstEmptyRow, startColTarget, dataToCopy.length, dataToCopy[0].length).setValues(dataToCopy);
    }
    
    function exportTriggerFunction()
    {
      var today = new Date();
      var lastDayOfMonth = new Date(today.getFullYear(), today.getMonth()+1, 0);
    
      if(today.getDate() == lastDayOfMonth.getDate() )
      {
    var exportSheetIds = 
      ["560568812","1440952465","439649389","513481136",
       "1088500659","133657514","1618608630","802444630",
       "1834450484","657423004","682313931","1980296394","635144452"]; // Please set the sheet IDs you want to export.
    
      const ss = SpreadsheetApp.getActiveSpreadsheet();
      var sheet = ss.getActiveSheet();
      var name = sheet.getRange('Reference!M2').getDisplayValue();
      var destination = DriveApp.getFolderById("1HRchNqQ5_0LYzfULw1hWN_ALNuijsy2q"); // Google Drive Folder Location
    
      // 1. Copy the active Spreadsheet as a tempora Spreadsheet.
      const spreadsheet = SpreadsheetApp.getActiveSpreadsheet().copy('tmp');
      const sheets = spreadsheet.getSheets();
      const check = sheets.filter(s => exportSheetIds.includes(s.getSheetId().toString()));
      if (check.length == 0) {
        throw new Error("No export sheets.");
      }
    
      // 2. Convert the formulas to the texts.
      sheets.forEach(sheet => {
        if (exportSheetIds.includes(sheet.getSheetId().toString())) {
          const targetRange = sheet.getDataRange();
          targetRange.copyTo(targetRange, { contentsOnly: true });
        }
      });
    
      // 3. Delete/Exclude sheets: Index and Reference
      sheets.forEach(sheet => {
        if (!exportSheetIds.includes(sheet.getSheetId().toString())) {
          spreadsheet.deleteSheet(sheet);
        }
      });
      SpreadsheetApp.flush();
    
      // 4. Retrieve the blob from the export URL.
      const id = spreadsheet.getId();
      const xlsxBlob = UrlFetchApp.fetch(`https://docs.google.com/spreadsheets/export?id=${id}&exportFormat=xlsx`, { headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` } }).getBlob();
    
      // 5. Create the blob as a file.
      destination.createFile(xlsxBlob.setName(`${name}.xlsx`));
    
      // 6. Delete the temporate Spreadsheet.
      DriveApp.getFileById(id).setTrashed(true);
    
      }
    }
    

    Note

    References: