Como generar un PDF y enviarlo por email con Google Apps Script

 Hace un tiempo expliqué como generar un PDF de ciertas hojas seleccionadas con Google Apps Script. También os expliqué anteriormente como enviar un email con Google Apps Script.

Un lector del blog me ha comentado aquí  como podríamos unir ambas cosas y realmente me parece una idea fantástica!

Es bastante más fácil de lo que parece, aunque implica algo de código:

function onOpen()
{
   SpreadsheetApp.getUi().createMenu("SCRIPT")
                   .addItem("Print", "ExportPDF")
                   .addItem("Send", "SendPDF")
                   .addToUi();
}

function SendPDF()
{
  var sheet   = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("ToPrint");
  var email   = sheet.getRange("C2").getValue();
  var pdfFile = GeneratePDF();
  MailApp.sendEmail(email, 'Ejemplo de como enviar un PDF', 'Ahi tienes el PDF', {
     name: 'El script más util del mundo :D ',
     attachments: [pdfFile.getAs(MimeType.PDF)]
 });
}

function ExportPDF()
{
   var pdfFile = GeneratePDF();
   SpreadsheetApp.getUi().alert(pdfFile.getUrl());
}

function GeneratePDF() 
{
   var timestamp = Math.floor((new Date()).getTime() / 1000);
   
   var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
   var values      = spreadsheet.getSheetByName("ToPrint").getDataRange().getValues();
   var out         = SpreadsheetApp.create(spreadsheet.getName() + "-ToPrint-" + timestamp);
   var folder      = DriveApp.getFolderById(values[1][1]);
   
   SpreadsheetApp.flush();
   for(var i = 1 ; i < values.length ; ++i)
   {
      var tab = values[i][0];
      
      var sheet = spreadsheet.getSheetByName(tab);
      
      if(sheet == null || sheet == undefined)
      {
         SpreadsheetApp.getUi().alert("Unknown tab:" + tab);
         return;
      }
      
      sheet.copyTo(out).setName(sheet.getName()).getDataRange().setFontColor("black");
   }
   var first_sheet = out.getSheets()[0];
   out.deleteSheet(first_sheet);
   SpreadsheetApp.flush();
   
   var exportUrl = 'https://docs.google.com/spreadsheets/d/' + out.getId()
      + '/export?exportFormat=pdf&format=pdf'
      + '&size=FOLIO'                  //Tamaño del papel: 0-LETTER,1-TABLOID,2-LEGAL,3-STATEMENT,4-EXECUTIVE,5-FOLIO,6-A3,7-A4,8-A5,9-B4,10-B5
      + '&portrait=false'              //Orientación: False - Horizontal / True - Vertical
      + '&fitw=true'                   //Forzamos que quepa correctamente en el ancho de la hoja
      + '&scale=4'                     // Fit to Page?
      + '&top_margin=1.75'             //Margen Superior          
      + '&bottom_margin=1.75'          //Margen inferior         
      + '&left_margin=1.5'             //Margen Izquierdo          
      + '&right_margin=1.5'            //Margen Derecho        
      + '&sheetnames=false'            //Marcamos si se ha de poner el nombre de las hojas o no
      + '&printtitle=false'            //Marcamos si se ha imprimir el titulo del documento o no
      + '&pagenum=false'               //Indicamos si se de pintar el número de página
      + '&gridlines=true'              //Marcamos si queremos que se vean las lineas de las celdas o no   
      + '&fzr=FALSE'                   //Frozen: marcamos si las cabeceras se han de ir imprimiendo o no en las consecutivas hojas
      + '&id=' + out.getId();          //Identificador del tab
            
  var response = UrlFetchApp.fetch(exportUrl, {  muteHttpExceptions: true, headers: { Authorization: 'Bearer ' +  ScriptApp.getOAuthToken()  } } );
  
  var blob = response.getBlob();
  blob = blob.setName(out.getName());
  var pdfFile = DriveApp.createFile(blob);
  pdfFile.moveTo(folder);
    
  //Erase the temp spreadsheet
  DriveApp.getFileById(out.getId()).setTrashed(true);
  
  return pdfFile;
}

Básicamente he modificado el código de generación de PDF para que me devuelva el fichero y lo pueda enviar como adjunto en el email. 

Si lo miráis con detenimiento veréis que no es complicado.

El email donde se enviaran los documentos esta en la misma hoja de ToPrint que usamos para decir que tabs usaremos en la generación del PDF.

como enviar pdf como adjunto con google apps script

Creo que el código es fácil de entender, pero no dudéis en hacerme llegar vuestras sugerencias.

Nos vemos!




18 comentarios:

  1. Holaa, esto es justo lo que he estado buscando en varios videos y nadie explica! Al ejecutar el código me aparece un error al obtener el método o la propiedad getFolderById en el objeto DriveApp, alguien sabe como resolver esto? Gracias!

    ResponderEliminar
    Respuestas
    1. Me alegro que te sirva! Generalmente si el getFolderById no funciona es o bien por que no has puesto bien el Id, o bien por que no existe o bien por que no tienes permisos. Copia si quieres la linea que estas usando y la reviso

      Eliminar
    2. Oh sería genial si puedes echarle un vistazo!! Este es el link del documento https://docs.google.com/spreadsheets/d/1e_uhHmvZiXxirMqaLLkNkxwRUTAX_3CuBRi1FRnasNA/edit?usp=sharing

      muchas gracias!!

      Eliminar
  2. Hola! excelente blog, he aprendido mucho. Estoy tratando de crear un PDF y enviarlo por correo, utilicé los códigos, pero al ejecutar la función sendPdf me aparece el siguiente error:
    TypeError: Cannot read property 'getAs' of undefined

    código:

    function EnviarCorreo()
    {
    var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Servicio Express');
    var email = sheet.getRange("C21").getValue();
    var pdfFile = ExportPDF();
    MailApp.sendEmail(email, 'Servicio', 'A PDF', {
    name: 'Servicio Express',
    attachments: [pdfFile.getAs(MimeType.PDF)] });
    }

    Espero me puedan ayudar,
    Gracias :)

    ResponderEliminar
    Respuestas
    1. Asi sin más se me hace complicado, pero por lo que entiendo del error, cuando recibes un undefined es que el objeto que estas llamando no existe.
      Es decir, que pdfFile no existe, no contienen nada. Tienes la función ExportPDF en el código? ESta realmente devolviendo algo?

      Tal vez poner logs podria irte bien para debugar que está pasando.

      Siento no poder ser mucha más ayuda. Ya nos contarás como te ha ido.

      Nos vemos!

      Eliminar
    2. Estimado, muchas gracias por la respuesta.
      Cuando ejecuto function EnviarCorreo(), me crea el PDF con las definiciones que establecí en Export PDF, queda perfecto, guardado en mi carpeta Drive como PDF.
      El problema aparece al momento de adjuntar ese archivo.
      Ahí me aparece el error: "TypeError: Cannot read property 'getAs' of undefined"-

      Si bloqueo el /// attachments: [pdfFile.getAs(MimeType.PDF)], el correo se envía sin nada adjunto y se crea el PDF en mi carpeta.

      Si dejo como variable el libro completo, se envía el pdf por correo (sin las medidas que necesito y todas las hojas del libro) y no se guarda en mi carpeta drive.

      Adjunto el código completo:

      function ExportPDF()
      {
      var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
      var sheet = spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Servicio Express'), true);
      var range = spreadsheet.getRange('A1:N73').activate();


      var exportUrl = 'https://docs.google.com/spreadsheets/d/' + spreadsheet.getId()
      + '/export?exportFormat=pdf&format=pdf'
      + '&size=A4' //Tamaño del papel: 0-LETTER,1-TABLOID,2-LEGAL,3-STATEMENT,4-EXECUTIVE,5-FOLIO,6-A3,7-A4,8-A5,9-B4,10-B5
      + '&portrait=True' //Orientación: False - Horizontal / True - Vertical
      + '&fitw=true' //Forzamos que quepa correctamente en el ancho de la hoja
      + '&top_margin=0.5' //Margen Superior
      + '&bottom_margin=0.5' //Margen inferior
      + '&left_margin=0.5' //Margen Izquierdo
      + '&right_margin=0.5' //Margen Derecho
      + '&sheetnames=false' //Marcamos si se ha de poner el nombre de las hojas o no
      + '&printtitle=false' //Marcamos si se ha imprimir el titulo del documento o no
      + '&pagenum=false' //Indicamos si se de pintar el número de página
      + '&gridlines=true' //Marcamos si queremos que se vean las lineas de las celdas o no
      + '&fzr=FALSE' //Frozen: marcamos si las cabeceras se han de ir imprimiendo o no en las consecutivas hojas
      + '&gid=' + sheet.getSheetId() //Identificador del tab
      + '&r1=' + (range.getRow() - 1) //Margenes del rango seleccionado
      + '&r2=' + range.getLastRow()
      + '&c1=' + (range.getColumn() - 1)
      + '&c2=' + range.getLastColumn();

      //Hacemos la petición para pedir el PDF
      var response = UrlFetchApp.fetch(exportUrl, { muteHttpExceptions: true, headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() } } );

      //Creamos el PDF con el binario que nos devuelve la petición anterior
      var blob = response.getBlob();
      blob = blob.setName(spreadsheet.getName());
      var pdfFile = DriveApp.createFile(blob);


      }


      function EnviarCorreo()
      {
      var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Servicio Express');
      var email = sheet.getRange("C21").getValue();
      var pdfFile = ExportPDF();
      MailApp.sendEmail(email, 'Servicio', 'Adjunto informe', {
      name: 'Servicio Express',
      attachments: [pdfFile.getAs(MimeType.PDF)]

      });

      }

      Espero me puedan ayudar .. muchas gracias :)

      Eliminar
    3. Encontré la falle y logré solucionarlo con otro tutorial que tienes en tu blog! gracias!

      Eliminar
    4. Podrias por favor compartir cual era el error?

      Eliminar
  3. Hola muy buenas, muchas gracias ante todo.
    Me pasa es que en el pdf generado, en las casillas formuladas me pone #REF!, ¿hay forma de que tome los valores calculados?

    ResponderEliminar
    Respuestas
    1. No se exactamente el código como lo estas usando, pero a priori te diria dos cosas: o bien no le has dado el tiempo necesario al spreadsheet para que guarde la información o bien has de obtener los datos visuales del spreadsheet. Creo que lo que más te servirá serán estos links: https://www.tutorialesgoogleappscripts.com/2020/11/diferencias-entre-getvalues-y-getdisplayvalues-con-google-apps-script.html y https://www.tutorialesgoogleappscripts.com/2020/08/como-forzar-la-escritura-de-un-spreadsheet-con-google-apps-script.html Ya me diras como te va!

      Eliminar
  4. Hola quisiera saber como fusionar 2 pdf y enviarlo por correo ..no he encontrado códigos que me ayuden 😭

    ResponderEliminar
    Respuestas
    1. Hola, Lo que pides no es fácil de hacer con Google Apps Script, de hecho, creo que se puede hacer pero no te sabría decir como. Tal pillando los blobs de los pdfs y generando un nuevo fusionandolos, pero la verdad, es escapa de mi conocimiento.
      Si soy capaz de conseguirlo te lo digo.
      Nos vemos!

      Eliminar
  5. Hola, tengo una duda...la informacion que quiero llevar al pdf debe estar tambien en la hoja Toprint...o son hojas diferentes?
    Estoy confundida a penas estoy iniciando

    ResponderEliminar
    Respuestas
    1. No, son cosas diferentes. Te recomiendo que mires los diferentes tutoriales sobre PDFs que he ido escribiendo. Creo que hay alguno que tal vez te puede ayudar algo más que este artículo : https://www.tutorialesgoogleappscripts.com/p/apps-script-pdf.html Si no te aclaras dimelo y le buscamos una solución

      Eliminar
  6. Mil gracias por los dos tutoriales, un verdadero salvavidas!

    En mi caso, use el primer tutorial para adjuntar el archivo PDF al codigo de correo que ya tenia creado, pero sin usar una copia de mi spreadsheet. Supongo que dependerá de cada uno el hacer uso de un spreadsheet auxiliar o no.


    Reitero mi agradecimiento por tu trabajo en este blog, saludos!

    ResponderEliminar
    Respuestas
    1. Me alegro que te sirva. Si no fuera mínimamente útil este blog no tendría ningún sentido. Gracias a ti por leerme.

      Eliminar
  7. Hola, cómo estás? Traté de incluir el código de guardar PDF de un rango seleccionado en la función generatePDF pero no he tenido éxito. ¿Me echarías una mano?

    ResponderEliminar
  8. Hola, como podría incluir en el email que se envía la URL de la hoja sheet?.
    Gracias.

    ResponderEliminar

Tal vez te interese