Updated doc not attaching as PDF in Gmail - javascript

I'm using Google Apps Script to create a copy of a doc file and save data from spreadsheet to the new file, then send it to email as a PDF attachment. Everything is working as it should except the pdf file is showing the default values which was from the source file instead of updated values.
Here are my codes,
var lr = dataSheet.getLastRow();
var dataRange = dataSheet.getRange(lr, 1, 1, 3).getValues();
var firstName = dataRange[0][1];
var lastName = dataRange[0][2];
var email = dataRange[0][3];
var emailText = "Hi "+firstName+",\n\nThank you for signing up.";
var emailSubject = "Test PDF";
var fileTemplate = DriveApp.getFileById("FILE_ID"); // Get Template File Id
var fileCopied = fileTemplate.makeCopy("Doc Copy-"+new Date()).getId(); // Make a copy of the template and get the id of the new file.
var doc = DocumentApp.openById(fileCopied); // Get destination doc
var dbody = doc.getBody(); // get destination doc's body
// Replace the fields with values from sheet.
dbody.replaceText("First Name", firstName);
dbody.replaceText("Last Name", lastName);
dbody.replaceText("EmailAddress", email);
Utilities.sleep(1000);
// Send Email with PDF attachment
MailApp.sendEmail(email, emailSubject, emailText, {
attachments: [doc.getAs(MimeType.PDF)]
});
How can I get the updated doc in the mail as a PDF attachment?

I think, it can be done if we consider the 'files' as 'blobs', that is how Google treats the Google Docs files. Please see this example which converts and sends Google Spreadsheets as PDF files.
/* Send Spreadsheet in an email as PDF, automatically */
function emailSpreadsheetAsPDF() {
// Send the PDF of the spreadsheet to this email address
var email = "amit#labnol.org";
// Subject of email message
// The date time string can be formatted in your timezone using Utilities.formatDate method
var subject = "PDF Reports - " + (new Date()).toString();
// Get the currently active spreadsheet URL (link)
// Or use SpreadsheetApp.openByUrl("<<SPREADSHEET URL>>");
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Email Body can be HTML too with your logo image - see ctrlq.org/html-mail
var body = "PDF generated using code at ctrlq.org from sheet " + ss.getName();
var url = ss.getUrl();
url = url.replace(/edit$/,'');
/* Specify PDF export parameters
// From: https://code.google.com/p/google-apps-script-issues/issues/detail?id=3579
exportFormat = pdf / csv / xls / xlsx
gridlines = true / false
printtitle = true (1) / false (0)
size = legal / letter/ A4
fzr (repeat frozen rows) = true / false
portrait = true (1) / false (0)
fitw (fit to page width) = true (1) / false (0)
add gid if to export a particular sheet - 0, 1, 2,..
*/
var url_ext = 'export?exportFormat=pdf&format=pdf' // export as pdf
+ '&size=letter' // paper size
+ '&portrait=false' // orientation, false for landscape
+ '&fitw=true&source=labnol' // fit to width, false for actual size
+ '&sheetnames=false&printtitle=false' // hide optional headers and footers
+ '&pagenumbers=false&gridlines=false' // hide page numbers and gridlines
+ '&fzr=false' // do not repeat row headers (frozen rows) on each page
+ '&gid='; // the sheet's Id
var token = ScriptApp.getOAuthToken();
var sheets = ss.getSheets();
//make an empty array to hold your fetched blobs
var blobs = [];
for (var i=0; i<sheets.length; i++) {
// Convert individual worksheets to PDF
var response = UrlFetchApp.fetch(url + url_ext + sheets[i].getSheetId(), {
headers: {
'Authorization': 'Bearer ' + token
}
});
//convert the response to a blob and store in our array
blobs[i] = response.getBlob().setName(sheets[i].getName() + '.pdf');
}
//create new blob that is a zip file containing our blob array
var zipBlob = Utilities.zip(blobs).setName(ss.getName() + '.zip');
//optional: save the file to the root folder of Google Drive
DriveApp.createFile(zipBlob);
// Define the scope
Logger.log("Storage Space used: " + DriveApp.getStorageUsed());
// If allowed to send emails, send the email with the PDF attachment
if (MailApp.getRemainingDailyQuota() > 0)
GmailApp.sendEmail(email, subject, body, {attachments:[zipBlob]});
}
More in here: Convert and Email Google Spreadsheets as PDF Files

Related

sendEmail for each item in array does not skip item when email address is incorrect [duplicate]

This question already has answers here:
How can I validate an email address in JavaScript?
(79 answers)
Checking if an email is valid in Google Apps Script
(4 answers)
Closed 6 months ago.
I am making a google apps script where I do a forEach to loop through my data to create a pdf file and send this by email to that person. I noticed that when the email address is not correct, it does not fully skip that item: it sends the pdf generated to the next email address. For example, if my array of rows looks like this:
dataSet = [
[Jane, Doe, typoinemail#gmail.com],
[John, Doe, johndoe#gmail.com]
]
It will send the pdf with 'Jane Doe' to johndoe#gmail.com.
How can I make sure it fully skips the item with the invalid email and sends the correct pdf to the next person?
This is my code:
let pdfSheet = SpreadsheetApp.getActive().getSheetByName('Sheet2');
let setupSheet = SpreadsheetApp.getActive().getSheetByName('Sheet0');
function emailAllCertificates() {
let dataSheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
let rows = dataSheet.getDataRange().getValues(); //get an array of all rows
rows.forEach(function (row) {
if(row[2] == '' || row[2] == undefined) {
SpreadsheetApp.getUi().alert('No receiver found: please fill in the email address');
} else {
emailToUser(createPDF(row), row);
}
});
}
function createPDF(rowData) {
let spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); //get spreadsheet
let url = spreadsheet.getUrl(); //get url of spreadsheet
let programName = setupSheet.getRange('B3').getValue(); //get title from this sheet
pdfSheet.getRange('F11').setValue(rowData[0] + ' ' + rowData[1]); //put first and last name in pfd document
url = url.replace(/edit$/,''); //setup file url: remove the trailing 'edit'
//additional parameters to url for exporting the sheet as a pdf
let url_ext = 'export?exportFormat=pdf&format=pdf' +
//pfd settigns
'&gridlines=false' + //false = hide gridlines
'&gid=' + pdfSheet.getSheetId(); //sheet ID
let token = ScriptApp.getOAuthToken();
let response = UrlFetchApp.fetch(url + url_ext, {
headers: {
'Authorization': 'Bearer ' + token
}
});
return result = response.getBlob().setName(programName + '_certificate_test' + '.pdf');
}
function emailToUser(attachment, row){
let subject = "Your results";
let message = "See attached the PDF";
MailApp.sendEmail(row[2], subject, message,{attachments:[attachment]});
}

Converting a Google doc to PDF, then moving to another folder

I am working on script that will fill a Google doc template with cell values from a spreadsheet. A copy of the edited doc is created and converted into a PDF. I would then like for both the doc and the pdf to be moved to the specified folder. I am stuck on getting the pdf moved over to the folder. I created a function to move the docs, and it seems to work fine with the Google doc copy, but an error is being returned when the function runs for the pdf. The error seems to be coming from the moveFile function. I have also tried moving moveFile pdf call to fillLetter, but still get the same error. This is my first attempt at coding with Google script, so any advice would be greatly appreciated. Thank you!
function fillLetter() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];//Form Responses tab
var templateId = '1d-CKr_Xi27FrWsH6eWdUCWKyzrLjy3ivTjLkk_WyJ4s';//Test doc
//get candidates row number from user input
var ui = SpreadsheetApp.getUi();
var rowNum = ui.prompt("Please enter candidate row number");
//activates first column in selected row
var col = sheet.getRange("A"+ rowNum.getResponseText());
sheet.setActiveRange(col);
var candName = sheet.getActiveCell().getValue();
col = sheet.getRange("B"+ rowNum.getResponseText());
sheet.setActiveRange(col);
var location = sheet.getActiveCell().getValue();
//creates copy of template doc and renames
var lastName = candName.split(" ");
var filename = candName.substring(0,1) + lastName[lastName.length - 1] + ' - Offer Letter';
var newOffer = DriveApp.getFileById(templateId).makeCopy();
var offerId = newOffer.getId();
DriveApp.getFileById(offerId).setName(filename);
//gets body of template doc and replaces text
var doc = DocumentApp.openById(offerId);
var body = doc.getBody();
var todaysDate = Utilities.formatDate(new Date(), 'PST', 'MMMM dd, yyyy');
body.replaceText('{{Todays date}}', todaysDate);
body.replaceText('{{Candidate name}}', candName);
body.replaceText('{{Location}}', location);
doc.saveAndClose();
//Creates folder and moves newly created files
var folderName = DriveApp.createFolder(candName).getId();
convertPDF(offerId, folderName); // Line 52
moveFile(offerId, folderName);
}
function convertPDF(fileToCopy, folder) {
var docFolder = DriveApp.getFolderById(folder);
var copy = DriveApp.getFileById(fileToCopy);
//Add the PDF extension
var docblob = copy.getAs('application/pdf');
docblob.setName(copy.getName() + ".pdf");
var pdfFile = DriveApp.createFile(docblob);
var fileId = pdfFile.getId();
Logger.log("Offer PDF id - " + fileId);//for debug
moveFile(fileId, docFolder); // Line 66
}
function moveFile(fileId, folderId) {
var file = DriveApp.getFileById(fileId);
DriveApp.getFolderById(folderId).addFile(file); // Line 72: wh/ error is most likely occurring
}
Below is the error message from the Logger:
Error
Exception: Unexpected error while getting the method or property getFolderById on object DriveApp.
moveFile # testCode.gs:72
convertPDF # testCode.gs:66
fillLetter # testCode.gs:52
In the current stage, you can use moveTo method for moving the file on Google Drive. This has added on July 27, 2020. Ref In your script, when moveFile is modified, it becomes as follows.
Modified script:
function moveFile(fileId, folderId) {
var file = DriveApp.getFileById(fileId);
file.moveTo(DriveApp.getFolderById(folderId));
}
Reference:
moveTo(destination)
The methods of addFile, addFolder, removeFile and removeFolder have already been deprecated. Ref

google.visualization.query - filter spreadsheet data column based on a json.parse parameter

I have 2 google webapps.
First one contains 2 textboxes where users insert username and password. If accepted, these informations pass to the 2nd app url.
The 2nd app extracts with google.visualization.query, data from a google spreadsheet. I can use select and where query without problems with fixed values like strings or numbers but I can't use informations cointained in app url.
My intention is to query my spreadsheet based on mail address contained in app url, so I can show to users only their own data.
Below the script in my html file:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="https://www.gstatic.com/charts/loader.js"></script>
<script>
google.load('visualization', '1.0', {packages: ['corechart','table']});
google.setOnLoadCallback(loadEditor);
function loadEditor() {
var queryString = encodeURIComponent("select A, T where T="");
var SSID = "xxxxxxxxxMPgJx3gQ" //My spreadsheet ID
var SHEET_NAME = "FOGLIO ORE"
var query = new google.visualization.Query('https://spreadsheets.google.com/tq?key='+SSID+'&sheet='+SHEET_NAME+'&oauth_token=<?=ScriptApp.getOAuthToken()?>&headers=1&tq=' + queryString);
query.send(handleSampleDataQueryResponse);
}
function handleSampleDataQueryResponse(response) {
console.log(response);
var data = response.getDataTable();
var chartTbl = new google.visualization.Table(document.getElementById('dataTable'));
chartTbl.draw(data);
}
</script>
Here is the doGet:
function doGet(request) {
var params = JSON.parse(JSON.stringify(request));
var uname = params.parameter.username;
var email = params.parameter.email;
var imgUrl = params.parameter.imgUrl;
var default_imgUrl = "https://www.pngfind.com/pngs/m/381-3819326_default-
avatar-svg-png-icon-free-download-avatar.png";
var html = HtmlService.createTemplateFromFile('HTML').evaluate().getContent();
html = html.replace(default_imgUrl, imgUrl);
html = html.replace('<a id="user_name">User Name</a>','<a id="user_name">' +
uname + '</a>');
html = html.replace('<a id="email">email</a>','<a id="email">' + email +
'</a>');
return HtmlService.createHtmlOutput(html)
.setTitle('PRESENZE | ' + uname);
output.token = ScriptApp.getOAuthToken();
};
Answer:
You need to open the spreadsheet using SpreadsheetApp().openById() to extract the information to insert into your HTML table, in a doGet() function after publishing the script as a Web App.
Example:
In this example I'll use a Spreadsheet which looks like the sheet you linked in the comments:
A
B
C
D
E
F
G
1
Employee
Date
In
Out
Type
Place
Email
2
John G.
21/04/2021
9.00
12.00
holidays
Ancona
john.g#mymail.org
3
Neil Geiman
22/04/2021
9.00
15.00
work
Pesaro
neil.geiman#mymail.org
4
Penny Lane
23/04/2021
9.00
15.00
holidays
Ancona
penny.lane#mymail.org
5
Buddy Holly
24/04/2021
9.00
15.00
work
Pesaro
buddy.holly#mymail.org
6
Neil Geiman
22/04/2021
8.00
15.00
work
Ancona
neil.geiman#mymail.org
7
Penny Lane
27/04/2021
12.00
15.00
work
Pesaro
penny.lane#mymail.org
In the doGet() function, you will want to use the event object to extract the parameters sent in the URL, as you are already doing:
function doGet(e) {
const uname = params.parameter.username
const email = params.parameter.email
const imgUrl = params.parameter.imgUrl
// ...
}
You will then need to open the Spreadsheet with the data programmatically to extract the data:
// ...
const ss = SpreadsheetApp.openById("xxxxxxx")
// ...
You will need to replace the xxxxxxx with the ID of the Spreadsheet.
From here, knowing the columns, you can extract out the information you need:
// ...
const sheet = ss.getSheetByName("Hours sheet") // use actual sheet name
const emailColumn = 7 // column G = column 7
const range = sheet.getDataRange().getValues()
// ...
and then filter the rows you want based on the email parameter you were passed:
// ...
const rows = [] // initialise an array to store the correct rows
for (let i = 1; i < range.length: i++) { // start from one to skip the header
if (range[i][emailColumn] == e.parameter.email) {
rows.push(range[i])
}
}
console.log(rows)
// ...
So here you will log all the rows as a 2D array which have an email corresponding to the one in the parameter. For the above sheet, using email=penny.lane#mymail.org as a URL parameter will log the following:
[
["Penny Lane", "23/04/2021", "9.00", "15.00", "holidays", "Ancona", "penny.lane#mymail.org"],
["Penny Lane", "27/04/2021", "12.00", "15.00", "work", "Pesaro", "penny.lane#mymail.org"]
]
Which you can use to form your HTML table.
References:
Class SpreadsheetApp | Apps Script | Google Developers
Web Apps | Apps Script | Google Developers

Request failed for https://docs.google.com returned code 401 - saving and emailing PDF files from Google Sheets

I have basically non-existent experience in Javascript, but know a little bit of Python so I figured I was up to the task of Frankensteining a couple of pre-made scripts together which I found online. The idea is to look through a list of data, then send PDFs of the appropriate spreadsheet to the desired e-mail address. I have copied my attempt below.
// This constant is written in for rows for which an email has been sent successfully.
var EMAIL_SENT = 'EMAIL_SENT';
function sendEmails2() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 16; // First row of data to process
var numRows = 1; // Number of rows to process
var dataRange = sheet.getRange(startRow, 1, numRows, 6); // Fetch the range of cells
var data = dataRange.getValues(); // Fetch values for each row in the Range.
const token = ScriptApp.getOAuthToken();
const ss = SpreadsheetApp.getActiveSpreadsheet(); // Get the currently active spreadsheet URL (link)
const subject = 'Monthly Invoice'; // Subject of email message
const url = 'https://docs.google.com/spreadsheets/d/SS_ID/export?'.replace('SS_ID', ss.getId()); // Base URL
const exportOptions = // Specify PDF export parameters From: https://code.google.com/p/google-apps-script-issues/issues/detail?id=3579
'exportFormat=pdf&format=pdf' + // export as pdf / csv / xls / xlsx
'&size=A4' + // paper size legal / letter / A4
'&portrait=true' + // orientation, false for landscape
'&fitw=true&source=labnol' + // fit to page width, false for actual size
'&sheetnames=false&printtitle=false' + // hide optional headers and footers
'&pagenumbers=false&gridlines=false' + // hide page numbers and gridlines
'&fzr=false' + // do not repeat row headers (frozen rows) on each page
'&gid='; // the sheet's Id
const sheets = ss.getSheets();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var emailAddress = row[4];
var message = row[3];
var emailSent = row[5];
var client_id = row[0];
var client_sheet = ss.getSheetByName(client_id);
if (emailSent !== EMAIL_SENT) { // Prevents sending duplicates
const blobs = []; // make an empty array to hold your fetched blobs
// Convert individual worksheets to PDF
const response = UrlFetchApp.fetch(url + exportOptions + client_sheet, {
headers: {
Authorization: 'Bearer ${token}'
}
});
// convert the response to a blob and store in our array
blobs[i] = response.getBlob().setName('${client_sheet}.pdf');
// If allowed to send emails, send the email with the PDF attachment - 500 emails per day standard
if (MailApp.getRemainingDailyQuota() > 0)
GmailApp.sendEmail(emailAddress, subject, message, {
attachments: [blobs[i]]
});
sheet.getRange(startRow + i, 6).setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
// create new blob that is a zip file containing our blob array
// const zipBlob = Utilities.zip(blobs).setName(`${ss.getName()}.zip`);
// optional: save the file to the root folder of Google Drive
// DriveApp.createFile(zipBlob);
}
I'm currently running into this error, however - and honestly I'm lost.
Request failed for https://docs.google.com returned code 401
Request failed for https://docs.google.com returned code 401. Truncated server response: <HTML> <HEAD> <TITLE>Unauthorized</TITLE> </HEAD> <BODY BGCOLOR="#FFFFFF" TEXT="#000000"> <H1>Unauthorized</H1> <H2>Error 401</H2> </BODY> </HTML> (use muteHttpExceptions option to examine full response) (line 39, file "send_emails")
If it helps, line 39 is:
const response = UrlFetchApp.fetch(url + exportOptions + client_sheet, {
Could somebody please assist? Thank you.
If you are using the script in your question, how about this answer? Please think of this as just one of several answers.
Modification point:
Unfortunately, in the current stage, the template literal, which was added at ES2015, cannot be used with Google Apps Script. I thought that the reason of your issue might be this.
Modified script:
Please modify your script as follows.
From:
Authorization: 'Bearer ${token}'
To:
Authorization: 'Bearer ' + token
And
From:
blobs[i] = response.getBlob().setName('${client_sheet}.pdf');
To:
blobs[i] = response.getBlob().setName(client_sheet + '.pdf');
References:
Basic JavaScript features
Template literals
If I misunderstood your question and this was not the result you want, I apologize.
Added:
I noticed one more modification point. Please modify your script as follows.
From:
var client_sheet = ss.getSheetByName(client_id);
To:
var client_sheet = ss.getSheetByName(client_id).getSheetId();
In order to retrieve gid, please use getSheetId().
Updated: December 19, 2020:
Now, Google Apps Script can use V8 runtime. Ref So the template literals can be used. But there is an important point. In this case, please use the backtick (grave accent) as follows.
Authorization: `Bearer ${token}`
and
blobs[i] = response.getBlob().setName(`${client_sheet}.pdf`);

Google Script to automatically draft emails

My goal is to create email drafts automatically from a Google Sheets list. Everything works mostly, the only problem I am having is that the message in the email just says [object Object].
The subject is fine, the email address is fine, and the break to keep it from sending to the same person again is fine.
I am not sure what is wrong.... but I am guessing it starts at //Build the email message.
Any suggestions?
I have the following:
var EMAIL_DRAFTED = "EMAIL DRAFTED";
function draftPastDueEmails() {
var sheet = SpreadsheetApp.getActiveSheet(); // Use data from the active sheet
var startRow = 2; // First row of data to process
var numRows = sheet.getLastRow() - 1; // Number of rows to process
var lastColumn = sheet.getLastColumn(); // Last column
var dataRange = sheet.getRange(startRow, 1, numRows, lastColumn) // Fetch the data range of the active sheet
var data = dataRange.getValues(); // Fetch values for each row in the range
// Work through each row in the spreadsheet
for (var i = 0; i < data.length; ++i) {
var row = data[i];
// Assign each row a variable
var clientName = row[12]; // Client name
var clientEmail = row[14]; // Client email
var reference = row[1]; // Account Reference
var invNum = row[0]; // Invoice number
var emailStatus = row[20]; // Email Status
// Prevent from drafing duplicates and from drafting emails without a recipient
if (emailStatus !== EMAIL_DRAFTED && clientEmail) {
// Build the email message
var emailBody = '<p>Hello ' +clientName+ ',</p>';
emailBody += '<p>I hope all is well.</p>';
emailBody += '<p>I noticed that invoice <strong>' +invNum+ '</strong> is currently past due.</p>';
emailBody += '<p>Could you please take a moment to check your records to see if a payment has been made and provide a receipt to allow me to track it in my system.</p>';
emailBody += '<p>If no payment has been made for whatever reason, please do so as soon as possible so I can post it to your account.</p>';
emailBody += '<p>Please let me know if you have any questions.</p>';
emailBody += '<p>Best,</p>';
// Create the email draft
GmailApp.createDraft(
clientEmail, // Recipient
'Past due account - '+reference+ // Subject
'',
{
htmlBody: emailBody // Options: Body (HTML)
}
);
sheet.getRange(startRow + i, lastColumn).setValue(EMAIL_DRAFTED); // Update the last column with "EMAIL_DRAFTED"
SpreadsheetApp.flush(); // Make sure the last cell is updated right away
}
}
}
It looks like the problem is here:
// Create the email draft
GmailApp.createDraft(
clientEmail, // Recipient
'Past due account - '+reference+ // Subject
'',
{
htmlBody: emailBody // Options: Body (HTML)
}
);
The method is expecting 3 strings -- a recipient, subject and a body -- followed by options with your "htmlBody" parameter. The code above only provides a recipient and a subject. Try this instead:
// Create the email draft
GmailApp.createDraft(
clientEmail, // Recipient
'Past due account - '+reference, // Subject
'', // Plaintext body
{
htmlBody: emailBody // Options: Body (HTML)
}
);

Categories