Google Sheets Email - Vlookup showing REF value in PDF - google-apps-script

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);

Related

Email script is not sending the correct sheet from Google spreadsheet

I have the code below which creates a duplicate sheet, adds some data and then sends that new sheet as an email attachment. The issue is that the attachment is the sheet called sending ALL sheets and not solely the newly created sheet. I think what i need to do is set the new sheet as active setActiveSheet or Activate, but neither of these have worked for me.
var OrderSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("OTW Sheet");
var Row = OrderSheet.getLastRow();
var Values1 = OrderSheet.getRange(Row, 5).getValue();
var Values2 = OrderSheet.getRange(Row, 6).getValue();
var Values3 = OrderSheet.getRange(Row, 7).getValue();
var Values4 = OrderSheet.getRange(Row, 8).getValue();
var Values5 = OrderSheet.getRange(Row, 9).getValue();
var Values6 = OrderSheet.getRange(Row, 2).getValue();
var Invoice = SpreadsheetApp.openById("13JpYjMNg5CcGwnRX_ZMXJIw5jeGfUrVvAC47xCNzHdg");
Invoice.getSheetByName("BLANK").copyTo(Invoice).setName("OTW Invoice " + new Date).activate();
var Newsheet = Invoice.getActiveSheet();
Newsheet.getRange(17, 2).setValue(Values1);
Newsheet.getRange(18, 2).setValue(Values2);
Newsheet.getRange(19, 2).setValue(Values3);
Newsheet.getRange(20, 2).setValue(Values4);
Newsheet.getRange(21, 2).setValue(Values5);
Newsheet.getRange(6, 7).setValue(Values6);
var message = {
to: "wilson.aidan101#gmail.com",
subject: "Test Invoice",
body: "Hi,\n\nPlease find the latest invoice attached.\n\nThank you,\nAidan",
name: "Aidan",
attachments: [Invoice.getAs(MimeType.PDF).setName("Invoice")]
}
MailApp.sendEmail(message);
OrderSheet.getRange(Row, 1).setValue("COMPLETE");
`
As a guess, try to add:
Invoice.moveActiveSheet(1);
Before this line:
var Newsheet = Invoice.getActiveSheet();
Perhaps you see only first sheet in the PDF. So it makes sense to make the 'Newsheet' a first sheet.

"CELLIMAGE" text displayed in place of image upon export pdf script from Google sheets

Hi there those way cleverer than me!
I have a Google script to export a pdf from a Google sheet which works fine. However I have an image in a cell that is not displayed in the exported pdf, instead it just says "CELLIMAGE". If I manually export the pdf it displays the image correctly...
My code is:
function exportFl() {
var sheetName = "FL"
//var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
//var allsheets = ss.getSheets();
//for (var s in allsheets){
//var sheet=allsheets[s]
//var maxRows = ss.getMaxRows();
//var lastRow = ss.getLastRow();
//if (maxRows-lastRow != 0){
//ss.deleteRows(lastRow+1, maxRows-lastRow);}
var value = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName).getRange('E7').getValue();
var folderID = "1x0tBqOP07e2XIzXHgvYqAaBiu7Zd7DfV"; // Folder id to save in a folder.
var timeZone = Session.getScriptTimeZone();
date = Utilities.formatDate(new Date(), timeZone, "YYMMdd");
var pdfName = value+"_FSL Conformance_"+ date;
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);
//save to pdf
// --- I added below script.
var r = destSheet.getDataRange();
var startRow = r.getNumRows() + 1;
var number = destSheet.getMaxRows() - startRow + 1;
if (number > 0) destSheet.deleteRows(startRow, number);
SpreadsheetApp.flush(); // This might not be required to be used.
// ---
var theBlob = destSpreadsheet.getBlob().getAs('application/pdf').setName(pdfName);
var newFile = folder.createFile(theBlob);
//Delete the temporary sheet
DriveApp.getFileById(destSpreadsheet.getId()).setTrashed(true);
}
Anyone got any ideas??
Thanks
It seems that in your script, the cells are overwritten by destRange.setValues(sourcevalues);. I thought that this might be the reason of your issue. And, in your script, the values have already been copied by var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(sourceSpreadsheet.getId()).makeCopy("tmp_convert_to_pdf", folder)). So in this case, how about the following modification?
In this modification, I used the following flow.
Copy the 1st tab.
Overwrite the data to the copied sheet.
Copy the images in the cells to the copied sheet.
Delete the temporal sheet.
When this flow is reflected to your script, it becomes as follows.
Modified script:
From:
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);
To:
var tempSheet = destSpreadsheet.getSheets()[0];
// 1. Copy the 1st tab.
var destSheet = tempSheet.copyTo(destSpreadsheet);
// 2. Overwrite the data to the copied sheet.
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);
// 3. Copy the images in the cells to the copied sheet.
sourcevalues.forEach((r, i) => {
r.forEach((c, j) => {
if (c.toString() == "CellImage") {
tempSheet.getRange(i + 1, j + 1).copyTo(destSheet.getRange(i + 1, j + 1), {contentsOnly: true}); // Modified
}
});
});
// 4. Delete the temporal sheet.
destSpreadsheet.deleteSheet(tempSheet);
References:
copyTo(spreadsheet) of Class Sheet
copyTo(destination) of Class Range

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);
}

PDF in other google drive

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);
}

Copy Value and Format to new sheet in Google sheets using a script

In Google Apps Script, Im using the script below to create a pdf and send as an email.
The problem is that when it creates the new sheet and copies across, the formulas are copied. The emailed PDF than shows a REF error.
I need to copy the Value and Format.
Thanks
function exportPDF() {
var originalSpreadsheet = SpreadsheetApp.getActive();
var message = "Please see attached";
var projectname = originalSpreadsheet.getRange("a2:c2").getValues();
var period = originalSpreadsheet.getRange("B24:c24").getValues();
var subject = projectname + " - Daily Control Totals - " + period;
var contacts = originalSpreadsheet.getSheetByName("Contacts");
var numRows = contacts.getLastRow();
var emailTo = contacts.getRange(2, 2, numRows, 1).getValues();
var newSpreadsheet = SpreadsheetApp.create("Spreadsheet to export");
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var projectname = SpreadsheetApp.getActiveSpreadsheet();
sheet = originalSpreadsheet.getActiveSheet();
sheet.copyTo(newSpreadsheet);
newSpreadsheet.getSheetByName('Sheet1').activate();
newSpreadsheet.deleteActiveSheet();
DriveApp.getFileById(newSpreadsheet.getId()).getAs('application/pdf').getBytes();
var attach = {fileName:'Daily Totals.pdf',content:pdf, mimeType:'application/pdf'};
MailApp.sendEmail(emailTo, subject, message, {attachments:[attach]});
DriveApp.getFileById(newSpreadsheet.getId()).setTrashed(true);}
Currently, if you have references to other sheets in the sheet you are copying, after copying you will get REF errors, which makes sense as the data is no longer where it should be.
To avoid that when we can use Range's copyTo(Range, Object); method. However, this method can only be used within a single spreadsheet, so we need to create a temporary sheet that will store our values then copy the sheet to a new spreadsheet to send in an email:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var newSS = SpreadsheetApp.create("Spreadsheet to export");
var temp = ss.duplicateActiveSheet(); //create temp sheet that lets us copy the values
range = temp.getDataRange();
range.copyTo(range, {contentsOnly: true}); //copy the values
temp.copyTo(newSS); //copy the values to a new spreadsheet
ss.deleteSheet(temp); //delete our temp sheet.
Updated: So your code would look something like this:
function exportPDF() {
var originalSpreadsheet = SpreadsheetApp.getActive();
var message = "Please see attached";
var projectname = originalSpreadsheet.getRange("a2:c2").getValues();
var period = originalSpreadsheet.getRange("B24:c24").getValues();
var subject = projectname + " - Daily Control Totals - " + period;
var contacts = originalSpreadsheet.getSheetByName("Contacts");
var numRows = contacts.getLastRow();
var emailTo = contacts.getRange(2, 2, numRows, 1).getValues();
var newSpreadsheet = SpreadsheetApp.create("Spreadsheet to export");
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheet = originalSpreadsheet.getActiveSheet();
var temp = originalSpreadsheet.duplicateActiveSheet(); //create temp sheet that lets us copy the values
range = temp.getDataRange();
range.copyTo(range, {contentsOnly: true}); //copy the values
temp.copyTo(newSpreadsheet); //copy the values to a new spreadsheet
ss.deleteSheet(temp); //delete our temp sheet.
newSpreadsheet.getSheetByName('Sheet1').activate();
newSpreadsheet.deleteActiveSheet();
DriveApp.getFileById(newSpreadsheet.getId()).getAs('application/pdf').getBytes();
var attach = {fileName:'Daily Totals.pdf',content:pdf, mimeType:'application/pdf'};
MailApp.sendEmail(emailTo, subject, message, {attachments:[attach]});
DriveApp.getFileById(newSpreadsheet.getId()).setTrashed(true);
}