1

I have a spreadsheet that always has sheet named "Timesheet" and the person filling it out can create as many additional notes sheets as they need.

I need to have all sheets besides the "Timesheet" sheet converted to individual PDFs and emailed to an address.

I am having trouble creating a script that doesn't roll them into a single PDF. Because of this it also includes the "Timesheet" Sheet in the PDF.

I need each of the notes sheets to be converted to a single PDF, then all PDFs emailed as separate attachments.The person filling it out can also rename the notes to whatever they want so I cannot get the sheet by name.

I have some code that combines all sheets and renames it that I use for a different purpose, I will include it below if it is of any help.

Thank you in advance for any help

function emailGoogleSpreadsheetAsPDF() {

  // Send the PDF of the spreadsheet to this email address
  var email = "email@gmail.com"; 

  // Get the currently active spreadsheet URL (link)
  var ss = SpreadsheetApp.getActiveSpreadsheet();

  // Custom
  var name = ss.getRange("Timesheet!J6:K6").getValue();
  var agency = ss.getRange("Timesheet!B4:C4").getValue();

  // Date
  var today = new Date();
  var dd = today.getDate();
  var mm = today.getMonth() + 1; //January is 0!
  var yyyy = today.getFullYear();

  if (dd < 10) {
    dd = '0' + dd;
  }

  if (mm < 10) {
    mm = '0' + mm;
  }

  today = mm + '/' + dd + '/' + yyyy;

  // Subject of email message
  var subject = name + " has Submitted Their Timesheet and Notes"; 

  // Email Body can  be HTML too 
  var body = "This was submitted on " + today;

  var blob = DriveApp.getFileById(ss.getId()).getAs("application/pdf");

  blob.setName(name + "_" + agency + "_" + today + "_" + "timesheet_notes.pdf");

  // If allowed to send emails, send the email with the PDF attachment
  if (MailApp.getRemainingDailyQuota() > 0) 
    GmailApp.sendEmail(email, subject, body, {
      htmlBody: body,
      attachments:[blob]     
    });
}
TacoSteve
  • 31
  • 7

1 Answers1

1

Emailing Sheets of a Spreadsheet as Separate PDFs

I could not do this by just creating the blobs and putting them in an array. So I created separate PDF files for each page and then trashed them at the end. I also added and exclusion array so that you could exlude certain files from the entire process and only send the sheets that you wish.

The current version includes dialog prompts that follow the progress of the program as it creates files and sends the email. So it's not exactly like your program.

Also these lines is your program are difficult for me to understand because you have a 2 cell array but you using getValue() instead of getValues();

 var name = ss.getRange("Timesheet!J6:K6").getValue();
 var agency = ss.getRange("Timesheet!B4:C4").getValue();

Here's my code:

function savePDFFiles1() {
  var ss=SpreadsheetApp.getActive();
  var exclA=['Summary','Images','Globals','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21'];
  var fA=[];
  var html="";
  var shts=ss.getSheets();
  var pdfFldr=DriveApp.getFolderById('FolderId');//folder where I stored the files temporarily
  for(var i=0;i<shts.length;i++) {
    var sh=shts[i];
    var name=sh.getName();
    if(exclA.indexOf(name)==-1) {
      sh.showSheet();
      for(var j=0;j<shts.length;j++) {
        if(shts[j].getName()!=name) {
          shts[j].hideSheet();
        }
      }
      SpreadsheetApp.flush();//I dont know if this is required
      var file=pdfFldr.createFile(ss.getBlob().getAs('application/pdf').setName(Utilities.formatString('%s_%s.pdf',ss.getName(),name)));
      html+=Utilities.formatString('<br />File: %s Created',file.getName());
      var userInterface=HtmlService.createHtmlOutput(html);
      SpreadsheetApp.getUi().showModelessDialog(userInterface, 'Files Created')                                          
      fA.push(file);
    }
  }
  GmailApp.sendEmail('recipient email', 'Plot Reports', 'Plot Reports Attached', {attachments:fA})
  html+='<br />Email Sent';
  var userInterface=HtmlService.createHtmlOutput(html);
  SpreadsheetApp.getUi().showModelessDialog(userInterface, 'Files Created');                                          
  for(var i=0;i<fA.length;i++ ) {
    fA[i].setTrashed(true);
  }
  html+='<br />Files Trashed and Process Complete';
  html+='<script>window.onload=function(){google.script.host.close();}</script>';
  var userInterface=HtmlService.createHtmlOutput(html);
  SpreadsheetApp.getUi().showModelessDialog(userInterface, 'Files Created');                                          
}

In my example, I was using a sheet that had some sheets named with numbers and I typically use certain sheets as hash tables so I typically keep them hidden all of the time.

I'll go back and take a look at updating your script now.

Okay so this is what I think your script would look like:

function emailGoogleSpreadsheetAsPDF() {
  var email="email@gmail.com"; 
  var ss=SpreadsheetApp.getActive();
  var name=ss.getRange("Timesheet!J6").getValue();//trimmed the range down to match the getValue();
  var agency=ss.getRange("Timesheet!B4").getValue();//same here
  var fldr=DriveApp.getFolderById('folderId');
  var fA=[];
  var today=Utilities.formatDate(new Date(),Session.getScriptTimeZone(),"MM/dd/yyyy");
  var subject=Utilities.formatString('%s has Submitted Their Timesheet and Notes',name); 
  var body=Utilities.formatString('This was submitted on %s',today);
  var shts=ss.getSheets();
  for(var i=0;i<shts.length;i++) {
    var sh=shts[i];
    var name=sh.getName();
    sh.showSheet();
    for(var j=0;j<shts.length;j++) {
      if(shts[j].getName()!=name) {
        shts[j].hideSheet();
      }
    }
    SpreadsheetApp.flush();//this may not be necessary...not sure
    var file=fldr.createFile(ss.getBlob().getAs('application/pdf')).setName(Utilities.formatString('%s_%s_%s_timesheet_notes.pdf', name,agency,today));
    fA.push(file);
  }
  GmailApp.sendEmail(email,subject,body, {attachments:fA});
  for(var i=0;i<fA.length;i++) {
    fA[i].setTrashed(true); 
  }
}

Note: I have not tested this last one so a little debugging may be required but it's basically the same idea as the other example. Which was tested.

There's another way to do this using UrlFetchApp that's discussed here Personally, I'd rather just create the files and trash them at the end.

With Requested Changes:

function emailGoogleSpreadsheetAsPDF() {
  var email="email@gmail.com";
  var exclA=['TimeSheet'];//and others
  var ss=SpreadsheetApp.getActive();
  var name=ss.getRange("Timesheet!J6").getValue();//trimmed the range down to match the getValue();
  var agency=ss.getRange("Timesheet!B4").getValue();//same here
  var fldr=DriveApp.getFolderById('folderId');
  var fA=[];
  var today=Utilities.formatDate(new Date(),Session.getScriptTimeZone(),"MM/dd/yyyy");
  var subject=Utilities.formatString('%s has Submitted Their Timesheet and Notes',name); 
  var body=Utilities.formatString('This was submitted on %s',today);
  var shts=ss.getSheets();
  for(var i=0;i<shts.length;i++) {
    var sh=shts[i];
    var name=sh.getName();
    if(exclA.indexOf(name)==-1) {
      sh.showSheet();
      for(var j=0;j<shts.length;j++) {
        if(shts[j].getName()!=name) {
          shts[j].hideSheet();
        }
      }
      SpreadsheetApp.flush();//this may not be necessary...not sure
      var file=fldr.createFile(ss.getBlob().getAs('application/pdf')).setName(Utilities.formatString('%s_%s_%s_timesheet_notes.pdf', name,agency,today));
      fA.push(file);
    }
  }
  GmailApp.sendEmail(email,subject,body, {attachments:fA});
  for(var i=0;i<fA.length;i++) {
    fA[i].setTrashed(true); 
  }
  for(var i=0;i<shts.length;i++) {
    if(exclA.indexOf(shts[i].getName())==-1) {
      shts[i].showSheet();
    }
  }
}

Adding a PDF of the Entire Spreadsheet(except for excluded hidden sheets):

function emailGoogleSpreadsheetAsPDF() {
  var email="email@gmail.com";
  var exclA=['TimeSheet'];//and others
  var ss=SpreadsheetApp.getActive();
  var name=ss.getRange("Timesheet!J6").getValue();//trimmed the range down to match the getValue();
  var agency=ss.getRange("Timesheet!B4").getValue();//same here
  var fldr=DriveApp.getFolderById('folderId');
  var fA=[];
  var today=Utilities.formatDate(new Date(),Session.getScriptTimeZone(),"MM/dd/yyyy");
  var subject=Utilities.formatString('%s has Submitted Their Timesheet and Notes',name); 
  var body=Utilities.formatString('This was submitted on %s',today);
  var shts=ss.getSheets();
  for(var i=0;i<shts.length;i++) {
    var sh=shts[i];
    var name=sh.getName();
    if(exclA.indexOf(name)==-1) {
      sh.showSheet();
      for(var j=0;j<shts.length;j++) {
        if(shts[j].getName()!=name) {
          shts[j].hideSheet();
        }
      }
      SpreadsheetApp.flush();//this may not be necessary...not sure
      var file=fldr.createFile(ss.getBlob().getAs('application/pdf')).setName(Utilities.formatString('%s_%s_%s_timesheet_notes.pdf', name,agency,today));
      fA.push(file);
    }
  }
  for(var i=0;i<shts.length;i++) {
    if(exclA.indexOf(shts[i].getName())==-1) {
      shts[i].showSheet();
    }
  }
  SpreadsheetApp.flush();//this may not be necessary...not sure
  var file=fldr.createFile(ss.getBlob().getAs('application/pdf')).setName(Utilities.formatString('%s.pdf',ss.getName()));
  fA.push(file)
  GmailApp.sendEmail(email,subject,body, {attachments:fA});
  for(var i=0;i<fA.length;i++) {
    fA[i].setTrashed(true); 
  }
}
Cooper
  • 59,616
  • 6
  • 23
  • 54
  • Wow, thank you for looking so much into this, I will test it tonight when I get home. And ya my code might have had some weird things in it, I am not a developer so it is a bunch of code I found online and stitched together. Thanks again! – TacoSteve May 23 '19 at 14:51
  • So it is working well so far, it send me all the PDFs as individuals. There are two small issues which I don't think will be hard fixes. The first is as it cycles through all the sheets it hides the others. At the end, the last sheet is shown with the rest hidden. I need it to still display all sheets that were showing. The second issue is the "Timesheet" sheet and two hidden sheets, that are used for data and settings, are being included in the email. The good thing is all the sheets I need excluded have fixed names. So is there some additional code you can add that excludes sheets by name? – TacoSteve May 24 '19 at 00:53
  • This worked beautifully! Thank you so so much. The last thing I went to do was I took some of my original code that bundles the entire spreadsheet into one PDF and added it to yours (I need both the bundle and the single PDFs sent). This is what I added: `var ssa = SpreadsheetApp.getActiveSpreadsheet(); var blob = DriveApp.getFileById(ssa.getId()).getAs("application/pdf"); blob.setName('name of file');` then I changed `{attachments:fA}` to `{attachments:[fA, blob]}` but get an _Invalid argument: attachments_ error. How do I send both attachments in my email? – TacoSteve May 24 '19 at 02:29
  • The problem with that is that the attachments are described as being an array of files not blobs. I think that just showing all of the sheets except for exclusions and pushing on the fA should do the trick. – Cooper May 24 '19 at 02:51
  • Oh that is a much better way of doing it. It all works so well! Thank you so much for helping, and also for optimizing my original code. I learned a lot from going through it. – TacoSteve May 24 '19 at 20:44