PDF in other google drive - google-apps-script

I have several tech's working with one google sheet.
Want to save the PDF from a filled sheet to a specific google drive (myPMtechs#gmail.com) // fake email only for indication // and not to my drive but keep the sheet in my drive.
For some reason the folder ID part does not work and I cannot find out why.
Script is
// Get active spreadsheet.
var sourceSpreadsheet = SpreadsheetApp.getActive();
// Get active sheet.
var sheets = sourceSpreadsheet.getSheets();
var sheetName = sourceSpreadsheet.getActiveSheet().getName();
var sourceSheet = sourceSpreadsheet.getSheetByName(sheetName);
// Output filename.
var jobNo = sourceSpreadsheet.getRange('A10:A10').getValue();
var jobName = sourceSpreadsheet.getRange('B3:B3').getValue();
var doorNo = sourceSpreadsheet.getRange('B5:B5').getValue();
var date = sourceSpreadsheet.getRange('A8:A8').getDisplayValue();
var pdfName = date + " - " + jobNo + " - " + jobName + " - " + doorNo;
// Get folder containing spreadsheet to save pdf in.
var parents = DriveApp.getFileById(sourceSpreadsheet.getId()).getParents();
if (parents.hasNext()) {
var folder = parents.next();
}
else {
folder = DriveApp.getRootFolder();
}
// Copy whole spreadsheet.
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(sourceSpreadsheet.getId()).makeCopy("tmp_convert_to_pdf", folder))
// Delete redundant sheets.
var sheets = destSpreadsheet.getSheets();
for (i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() != sheetName){
destSpreadsheet.deleteSheet(sheets[i]);
}
}
var destSheet = destSpreadsheet.getSheets()[0];
// Repace cell values with text (to avoid broken references).
var sourceRange = sourceSheet.getRange(1,1,sourceSheet.getMaxRows(),sourceSheet.getMaxColumns());
var sourcevalues = sourceRange.getValues();
var destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(),destSheet.getMaxColumns());destRange.setValues(sourcevalues);
// Save to pdf.
var theBlob = destSpreadsheet.getBlob().getAs('application/pdf').setName(pdfName);var newFile = folder.createFile(theBlob);
// Delete the temporary sheet.
DriveApp.getFileById(destSpreadsheet.getId()).setTrashed(true);
//Move the pdf file from rootfolder to the folder where the PMSheet are to be stored.
var files = DriveApp.getRootFolder().getFiles();
while (files.hasNext()) {
var file = files.next();
var destination = DriveApp.getFolderById("**Folder where files should go to**");
destination.addFile(file);
var pull = DriveApp.getRootFolder();
pull.removeFile(file);
}

Related

How can I overwrite the old file with app script when I save the file?

I have a few pages in google sheet. I'm getting the PDF output of Sheet2 from these pages with the code below. When the pdf prints out, it deletes the old pdf printout and creates a new one instead. When this happens, the links of the pdfs are changed. I don't want the link of the pdf to change. When it creates the new pdf I want it to overwrite the old one. How do I do this?
function checkSheet() {
var sheetName = "Sheet2";
var folderID = "folderid"; // Folder id to save in a folder.
var pdfName = "Catalog";
var sourceSpreadsheet = SpreadsheetApp.getActive();
var sourceSheet = sourceSpreadsheet.getSheetByName(sheetName);
var folder = DriveApp.getFolderById(folderID);
//Copy whole spreadsheet
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(sourceSpreadsheet.getId()).makeCopy("tmp_convert_to_pdf", folder))
//delete redundant sheets
var sheets = destSpreadsheet.getSheets();
for (i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() != sheetName){
destSpreadsheet.deleteSheet(sheets[i]);
}
}
var destSheet = destSpreadsheet.getSheets()[0];
//repace cell values with text (to avoid broken references)
var sourceRange = sourceSheet.getRange(1,1,sourceSheet.getMaxRows(),sourceSheet.getMaxColumns());
var sourcevalues = sourceRange.getValues();
var destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
destRange.setValues(sourcevalues);
//delete old pdf
var files = DriveApp.getFolderById(folderID).getFiles();
while (files.hasNext()) {
var file = files.next();
if(file.getName() == "Catalog"){
DriveApp.getFileById(file.getId()).setTrashed(true);
}
}
//save to pdf
var theBlob = destSpreadsheet.getBlob().getAs('application/pdf').setName(pdfName);
var newFile = folder.createFile(theBlob);
//Delete the temporary sheet
DriveApp.getFileById(destSpreadsheet.getId()).setTrashed(true);
}
Your question contains a lot of unnecessary details. I believe it could be simplified to, "How can one update an existing PDF file on Google Drive?"
To do this, you have to enable Advanced Drive Service which has an update method that will replace an existing PDF file in Google Drive.
function replaceFile(theBlob,theFileId) {
var oldFile = DriveApp.getFileById(theFileId);
Drive.Files.update({title: oldFile.getName(),mimeType:
oldFile.getMimeType()}, theFileId, theBlob);
}
}

Export Specific sheet to PDF [duplicate]

This question already has answers here:
Export Single Sheet to PDF in Apps Script
(3 answers)
Closed 2 years ago.
I am aware that similar type of question has being asked earlier under the Using Google Apps Script to save a single sheet from a spreadsheet as pdf in a specific folder
The code that was used is working for me and is mentioned below:
function generatePdf() {
// Get active spreadsheet.
var sourceSpreadsheet = SpreadsheetApp.getActive();
// Get active sheet.
var sheets = sourceSpreadsheet.getSheets();
var sheetName = sourceSpreadsheet.getActiveSheet().getName();
var sourceSheet = sourceSpreadsheet.getSheetByName(sheetName);
// Set the output filename as SheetName.
var pdfName = sheetName;
// Get folder containing spreadsheet to save pdf in.
var parents = DriveApp.getFileById(sourceSpreadsheet.getId()).getParents();
if (parents.hasNext()) {
var folder = parents.next();
} else {
folder = DriveApp.getRootFolder();
}
// Copy whole spreadsheet.
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(sourceSpreadsheet.getId()).makeCopy("tmp_convert_to_pdf", folder))
// Delete redundant sheets.
var sheets = destSpreadsheet.getSheets();
for (i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() != sheetName) {
destSpreadsheet.deleteSheet(sheets[i]);
}
}
var destSheet = destSpreadsheet.getSheets()[0];
// Repace cell values with text (to avoid broken references).
var sourceRange = sourceSheet.getRange(1, 1, sourceSheet.getMaxRows(), sourceSheet.getMaxColumns());
var sourcevalues = sourceRange.getValues();
var destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
destRange.setValues(sourcevalues);
// Save to pdf.
var theBlob = destSpreadsheet.getBlob().getAs('application/pdf').setName(pdfName);
var newFile = folder.createFile(theBlob);
// Delete the temporary sheet.
DriveApp.getFileById(destSpreadsheet.getId()).setTrashed(true);
}
The problem that I encounter is that my source sheet has a few fields which are auto populated in the sheet using vlookup. These fields are exported in the temp file which is created by this script as I have checked the trash and the field values are present but the PDF generated by the code is missing all the vlookup data.
The sample image of PDF is as below in which the vlookup missing entries are marked in yellow:
While the temp google sheet which is in trash has the values in the respective cells. Screenshot of the same is below the vlookup fields are highlighted in red.
Kindly help me with the solution.
Add the contentOnly: true in line 38. See the code. Hope this solves the problem. This will clear all the formula's. (im not sure the setValues() accept this extra parameter. the copyTo does, so you can make a copy of the sheet to a contentOnly sheet and then use that for the pdf)
function generatePdf() {
// Get active spreadsheet.
var sourceSpreadsheet = SpreadsheetApp.getActive();
// Get active sheet.
var sheets = sourceSpreadsheet.getSheets();
var sheetName = sourceSpreadsheet.getActiveSheet().getName();
var sourceSheet = sourceSpreadsheet.getSheetByName(sheetName);
// Set the output filename as SheetName.
var pdfName = sheetName;
// Get folder containing spreadsheet to save pdf in.
var parents = DriveApp.getFileById(sourceSpreadsheet.getId()).getParents();
if (parents.hasNext()) {
var folder = parents.next();
} else {
folder = DriveApp.getRootFolder();
}
// Copy whole spreadsheet.
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(sourceSpreadsheet.getId()).makeCopy("tmp_convert_to_pdf", folder))
// Delete redundant sheets.
var sheets = destSpreadsheet.getSheets();
for (i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() != sheetName) {
destSpreadsheet.deleteSheet(sheets[i]);
}
}
var destSheet = destSpreadsheet.getSheets()[0];
// Repace cell values with text (to avoid broken references).
var sourceRange = sourceSheet.getRange(1, 1, sourceSheet.getMaxRows(), sourceSheet.getMaxColumns());
var sourcevalues = sourceRange.getValues();
var destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
destRange.setValues(sourcevalues, {contentsOnly:true});
// Save to pdf.
var theBlob = destSpreadsheet.getBlob().getAs('application/pdf').setName(pdfName);
var newFile = folder.createFile(theBlob);
// Delete the temporary sheet.
DriveApp.getFileById(destSpreadsheet.getId()).setTrashed(true);
}
Just needed to add a line to flush the app did the work. Updated code:
function generatePdf() {
// Get active spreadsheet.
var sourceSpreadsheet = SpreadsheetApp.getActive();
// Get active sheet.
var sheets = sourceSpreadsheet.getSheets();
var sheetName = sourceSpreadsheet.getActiveSheet().getName();
var sourceSheet = sourceSpreadsheet.getSheetByName(sheetName);
// Set the output filename as SheetName.
var pdfName = sheetName;
// Get folder containing spreadsheet to save pdf in.
var parents = DriveApp.getFileById(sourceSpreadsheet.getId()).getParents();
if (parents.hasNext()) {
var folder = parents.next();
}
else {
folder = DriveApp.getRootFolder();
}
// Copy whole spreadsheet.
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(sourceSpreadsheet.getId()).makeCopy("tmp_convert_to_pdf", folder))
// Delete redundant sheets.
var sheets = destSpreadsheet.getSheets();
for (i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() != sheetName){
destSpreadsheet.deleteSheet(sheets[i]);
}
}
var destSheet = destSpreadsheet.getSheets()[0];
// Repace cell values with text (to avoid broken references).
var sourceRange = sourceSheet.getRange(1,1,sourceSheet.getMaxRows(),sourceSheet.getMaxColumns());
var sourcevalues = sourceRange.getValues();
var destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
destRange.setValues(sourcevalues);
SpreadsheetApp.flush();
// Save to pdf.
var theBlob = destSpreadsheet.getBlob().getAs('application/pdf').setName(pdfName);
var newFile = folder.createFile(theBlob);
// Delete the temporary sheet.
DriveApp.getFileById(destSpreadsheet.getId()).setTrashed(true);
}

Google Sheets Email - Vlookup showing REF value in PDF

Hoping you could help me with this, I have created a system that automatically sends the current sheet as an attachment in an email with body and subject text.
I have one problem that I cant seem to solve; the Vlookup used in the sheet generates either a blank column or a REF! on the emailed PDF. And any info directing to another value from a different sheet (eg ='Sheet2'!D2)
Here is the code used on the sheet triggering the PDF attachment :
function mailCCB() {
var originalSpreadsheet = SpreadsheetApp.getActive(); //get active spreadsheet
var message = "Good Day." + "\n\n" + "Please find attached certificate"; //email body
var certNo = originalSpreadsheet.getRange("C7").getValue(); //certificate number
var cust = originalSpreadsheet.getRange("E10").getValues(); //customer name
var subject = "Certificate " + certNo + " | " + cust; //email subject
var emailTo = originalSpreadsheet.getRange("E13").getValue(); //email address
var sheets = originalSpreadsheet.getSheets(); //get all sheets
var sheetName = originalSpreadsheet.getActiveSheet().getName(); //get current sheet name
var sourceSheet = originalSpreadsheet.getSheetByName(sheetName); //source sheet
// Get folder containing spreadsheet to save pdf in.
var parents = DriveApp.getFileById(originalSpreadsheet.getId()).getParents();
if (parents.hasNext()) {
var folder = parents.next();
}
else {
folder = DriveApp.getRootFolder();
}
SpreadsheetApp.getActiveSpreadsheet().toast("Working on it.. 50%", "Busy",12);
// Copy whole spreadsheet.
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(originalSpreadsheet.getId()).makeCopy("tmp_convert_to_pdf", folder))
// Delete redundant sheets.
var sheets = destSpreadsheet.getSheets();
for (i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() != sheetName){
destSpreadsheet.deleteSheet(sheets[i]);
}
}
var destSheet = destSpreadsheet.getSheets()[0];
var sourceRange = sourceSheet.getRange(1,1,sourceSheet.getMaxRows(),sourceSheet.getMaxColumns());
var sourcevalues = sourceRange.getValues();
SpreadsheetApp.getActiveSpreadsheet().toast("Working on it.. 60%", "Busy",12);
var destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
destRange.setValues(sourcevalues);
var theBlob = destSpreadsheet.getBlob().getAs('application/pdf').setName(subject);
var newFile = folder.createFile(theBlob);
//*****************************
SpreadsheetApp.getActiveSpreadsheet().toast("Working on it.. 75%", "Busy",12);
// Create a new Spreadsheet and copy the current sheet into it.
var newSpreadsheet = SpreadsheetApp.create("Spreadsheet to export");
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var projectname = SpreadsheetApp.getActiveSpreadsheet();
sheet = originalSpreadsheet.getActiveSheet();
sheet.copyTo(newSpreadsheet);
var pdf = DriveApp.getFileById(originalSpreadsheet.getId()).getAs('application/pdf').getBytes();
var attach = {fileName:'Klerkscale Certificate',content:pdf, mimeType:'application/pdf'};
MailApp.sendEmail(emailTo, subject, message, {attachments:[theBlob]});
DriveApp.getFileById(newSpreadsheet.getId()).setTrashed(true);
originalSpreadsheet.toast("Certificate will be sent shortly to " + emailTo,"Success",20);
}
This is the cropped received email result :
Ok I saw your problem, you're deleting all the sheets which are being referenced by the active sheet in destSpreadsheet thus they will not be available and the results will be #REF! for each cell referencing those deleted sheets. To solve that problem you could use the getDisplayValues() method, which retrieves the current displayed value of the cell instead of getValues() which retrieves the function(VLOOKUP in this case) set in the cell. Change this line:
var sourcevalues = sourceRange.getValues();
For this:
var sourcevalues = sourceRange.getDisplayValues();
Also, don't delete the other sheets which are being referenced until you have copyied the values to destRange in this line:
destRange.setValues(sourcevalues);

Preserve formula references when printing a single Google Sheets sheet to PDF

I want to accomplish the following:
Make a menu with a print to PDF button.
Have that button export a PDF to with the same name and same destination as the google sheet.
This works well, but the script I found I need to change, because most people print to PDF by making a temporary copy of the sheet, printing, and finally deleting the temporary copy.
I have references from other sheets in my original document that ends up printed as #REF! values because only the one sheet gets copied and printed, not my whole document.
How can i make this process include baking the formulas as text?
CODE FOR PRINTING:
function onOpen() {
var submenu = [{name: "Save PDF", functionName: "generatePdf"}];
SpreadsheetApp.getActiveSpreadsheet().addMenu('Export', submenu);
}
function generatePdf() {
// Get active spreadsheet.
var sourceSpreadsheet = SpreadsheetApp.getActive();
// Get active sheet.
var sheets = sourceSpreadsheet.getSheets();
var sheetName = sourceSpreadsheet.getActiveSheet().getName();
var sourceSheet = sourceSpreadsheet.getSheetByName(sheetName);
// Set the output filename as sheetName.
var pdfName = sheetName;
// Get folder containing spreadsheet to save pdf in.
var parents = DriveApp.getFileById(sourceSpreadsheet.getId()).getParents();
if (parents.hasNext()) {
var folder = parents.next();
}
else {
folder = DriveApp.getRootFolder();
}
// Copy whole spreadsheet.
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(sourceSpreadsheet.getId()).makeCopy("tmp_convert_to_pdf", folder))
// Delete redundant sheets.
var sheets = destSpreadsheet.getSheets();
for (i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() != sheetName){
destSpreadsheet.deleteSheet(sheets[i]);
}
}
var destSheet = destSpreadsheet.getSheets()[0];
// Replace cell values with text (to avoid broken references).
var sourceRange = sourceSheet.getRange(1, 1, sourceSheet.getMaxRows(), sourceSheet.getMaxColumns());
var sourcevalues = sourceRange.getValues();
var destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
destRange.setValues(sourcevalues);
// Save to pdf.
var theBlob = destSpreadsheet.getBlob().getAs('application/pdf').setName(pdfName);
var newFile = folder.createFile(theBlob);
// Delete the temporary sheet.
DriveApp.getFileById(destSpreadsheet.getId()).setTrashed(true);
}
CODE FOR CONVERTING FORMULAS TO TEXT:
function formulasAsText() {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for (var k = 0; k < sheets.length; k++) {
var range = sheets[k].getDataRange();
var values = range.getValues();
var formulas = range.getFormulas();
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[0].length; j++) {
values[i][j] = formulas[i][j] ? "'" + formulas[i][j] : values[i][j];
}
}
range.setValues(values);
}
}
To maintain formula references, you need to modify the formulasAsText() function to accept an input, and also not worry about writing a formula if one is found. This input could be a spreadsheet ID - i.e. the id of the temporary copy - or it could be an array of Sheet objects.
Once you have made these two modifications, you would call the function prior to deleting the non-desired sheets in the temporary copy:
/**
* #param Sheet[] wbSheets An array of Sheets to operate on.
* #param String toPreserve The name of the sheet which should be preserved.
*/
function preserveFormulas(wbSheets, toPreserve) {
if(!wbSheets || !wbSheets.length || !toPreserve)
throw new Error("Missing arguments.");
wbSheets.forEach(function (sheet) {
if ( sheet.getName() === toPreserve ) {
var range = sheet.getDataRange();
// Serialize the cell's current value, be it static or derived from a formula.
range.setValues(range.getValues());
}
});
}
Finished my script. Here is what i ended up with.
function onOpen() {
var submenu = [{name:"Save PDF", functionName:"generatePdf"}];
SpreadsheetApp.getActiveSpreadsheet().addMenu('Export', submenu);
}
function hideSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("sheet1");
var sheet2 = ss.getSheetByName("PRINT SHEET");
var sheet3 = ss.getSheetByName("sheet3");
var sheet4 = ss.getSheetByName("sheet4");
var sheet5 = ss.getSheetByName("sheet5");
var sheet6 = ss.getSheetByName("sheet6");
var sheet7 = ss.getSheetByName("sheet7");
sheet1.hideSheet();
sheet3.hideSheet();
sheet4.hideSheet();
sheet5.hideSheet();
sheet6.hideSheet();
sheet7.hideSheet();
}
function showSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("sheet1");
var sheet2 = ss.getSheetByName("PRINT SHEET");
var sheet3 = ss.getSheetByName("sheet3");
var sheet4 = ss.getSheetByName("sheet4");
var sheet5 = ss.getSheetByName("sheet5");
var sheet6 = ss.getSheetByName("sheet6");
var sheet7 = ss.getSheetByName("sheet7");
sheet1.showSheet();
sheet3.showSheet();
sheet4.showSheet();
sheet5.showSheet();
sheet6.showSheet();
sheet7.showSheet();
}
function generatePdf() {
// Get active spreadsheet.
var sourceSpreadsheet = SpreadsheetApp.getActive();
// Get active sheet.
var sheets = sourceSpreadsheet.getSheets();
var sheetName = sourceSpreadsheet.getActiveSheet().getName();
var sourceSheet = sourceSpreadsheet.getSheetByName(sheetName);
// Set the output filename as BID and number (from cell C4).
var pdfName = "BID " + sheets[0].getRange("C4").getValue() + ".pdf";
var ui = SpreadsheetApp.getUi();
// Get folder containing spreadsheet to save pdf in.
var parents = DriveApp.getFileById(sourceSpreadsheet.getId()).getParents();
if (parents.hasNext()) {
var folder = parents.next();
}
else {
folder = DriveApp.getRootFolder();
}
hideSheets();
// Save to pdf.
var theBlob = sourceSpreadsheet.getBlob().getAs('application/pdf').setName(pdfName);
var newFile = folder.createFile(theBlob);
showSheets();
ui.alert('Export finished');
}
Thanks.

How to Speed Up Function to Save 1 Sheet as PDF to Google Drive Folder in Google Sheets?

The below code works wonderfully to save only the 'PO Template' sheet of my Google Sheet file in PDF format to the designated Google Drive folder, 'Purchase_Orders'. The only issue is the function takes 10+ seconds to run. Is there something within the code that can be condensed to speed up the script run process for this one?
function checkSheet() {
var sheetName = "POTemplate";
var folderID = "Purchase_Orders";
var sourceSpreadsheet = SpreadsheetApp.getActive();
var sourceSheet = sourceSpreadsheet.getSheetByName(sheetName);
var poNo = sourceSheet.getRange("G5").getValue();
var shipTo = sourceSheet.getRange("E9").getValue();
var pdfName = shipTo + " Purchase Order " + poNo;
var folder = DriveApp.getFoldersByName(folderID);
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(sourceSpreadsheet.getId()).makeCopy(folder));
var source = destSpreadsheet.getSheetByName(sheetName).getDataRange();
source.copyTo(source, {contentsOnly: true});
var sheets = destSpreadsheet.getSheets();
for (i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() != sheetName){
destSpreadsheet.deleteSheet(sheets[i])}};
var theBlob =
destSpreadsheet.getBlob().getAs('application/pdf').setName(pdfName);
var newFile = folder.next().createFile(theBlob);
DriveApp.getFileById(destSpreadsheet.getId()).setTrashed(true);
var strUrl = newFile.getUrl()
var html = HtmlService.createTemplateFromFile("NewTab");
html.strUrl = strUrl
SpreadsheetApp.getUi().showModelessDialog(html.evaluate(),
"Congratulations! "+ pdfName + " is now saved.")
}
There is my Execution transcript of your code.
It seems File.makeCopy takes 50% of time. Also Folder.createFile wants 15%.
This means that your optimization will be minimal in the future.