Google Apps Script Exporting Blank Copy of Copied Template Spreadsheet - google-apps-script

I have a source spreadsheet with content.
I have a destination template for that content.
I select and copy the content in the source spreadsheet.
I create a copy of the destination template and paste in the source spreadsheet content.
I then execute some code to export the destination template as an XLSX file and attach it to an email.
The email and the attachment come through, but the contents of the XLSX file match the original template - the content I pasted in is missing.
Yet if I take my export string and run it through the browser, it exports the XLSX file just fine with the contents!
It appears the export function is running before the content paste is complete and sending the newly created destination template without the contents.
I've already tried Utilities.sleep(30000), but no matter how long I wait, I always get a blank copy of the original template. WTF?!
Complete code:
function sendVendor() {
// Open the Active Spreadsheet
var ssMaster = SpreadsheetApp.getActiveSpreadsheet();
var sheetInsert = ssMaster.getSheetByName('Insert RFQ');
var rfqNumber = sheetInsert.getRange('AW2').getValue();
var sheetWorkUp = ssMaster.getSheetByName('WORK UP');
var backgroundColor = '#FF5050';
var row = 11;
var newSheetRow = 13;
var numRows = 0;
var valuesPartNumbers = sheetWorkUp.getRange(12, 2, 233, 1).getValues();
var size = valuesPartNumbers.filter(function(value) { return value != '' }).length;
size = size - 1;
// Create the new RFQ from Template
var template = DocsList.getFileById('1M2f5yoaYppx8TYO_MhctEhKM_5eW-QCxlmJdjWg9VUs'); // Quote Workup Template
var newRFQ = template.makeCopy('Vendor RFQ Request ' + rfqNumber);
var newRFQId = newRFQ.getId();
var folderNew = DocsList.getFolder('Vendor RFQ Requests');
newRFQ.addToFolder(folderNew)
// Open new RFQ
var ssTemplate = SpreadsheetApp.openById(newRFQId);
var sheetVendorRequest = ssTemplate.getSheetByName('Vendor Request');
var newTemplateURL = ssTemplate.getUrl();
var needPricing = new Array();
var valuesFRCosts = sheetWorkUp.getRange(12, 8, size, 1).getValues();
for (var i = 0; i < valuesFRCosts.length; i++) {
row++;
if (valuesFRCosts[i][0] == '') {
var sheetWorkUpRow = sheetWorkUp.getRange(row, 1, 1, sheetWorkUp.getLastColumn());
sheetWorkUpRow.setBackground(backgroundColor);
var sendToTemplate = sheetWorkUp.getRange(row, 1, 1, 6).getValues();
sendToTemplate[0].splice(2, 1);
sheetVendorRequest.getRange(newSheetRow, 2, 1, 5).setValues(sendToTemplate);
newSheetRow++;
}
}
var url = 'https://docs.google.com/feeds/download/spreadsheets/Export?key=' + newRFQId + '&exportFormat=xlsx';
var doc = UrlFetchApp.fetch(url);
var attachments = [{fileName:'Vendor RFQ Request ' + rfqNumber, content: doc.getContent(),mimeType:"application/vnd.ms-excel"}];
MailApp.sendEmail("user#domain.com", "Excel Export Test", "See the attached Excel file.", {attachments: attachments});
}

The solution is to add SpreadsheetApp.flush(); prior to the export (just before var url). The SpreadsheetApp was still occupied with the template (in memory) after being copied - flushing it, and then exporting, forces the script to make a new call to the new Spreadsheet and get the latest data.
Thank you, Faustino Rodriguez.

Related

App Script to insert an image from google drive to a google doc

Have collected info through a google form to a google sheet. Part of that info is the path to an image stored on drive. I have an app script that replaces key words on a document with the data collected in the sheet. I can get it to replace a piece of text, {{image}} for example with the url stored but I cannot get it to actually put a copy of the image into the document.
Any suggestions.
Code below
// #ts-nocheck
function autoFillGoogleDocFromForm(e) {
//e.values is an array of form values
var timestamp = e.values[0];
var email = e.values[1];
var who = e.values[2];
var employeeorcon = e.values[3];
var location = e.values[4];
var roomorarea = e.values[5];
var type = e.values[6];
var dateofworks = e.values[7];
var imageofcompletedcon = e.values[8];
var checkedoffintext = e.values[9];
var checkname = e.values[10];
var checkco = e.values[11];
var checkeddate = e.values[12];
//file is the template file, and you get it by ID
var file = DriveApp.getFileById("1WcYKvsRFbKK73J-ep66mR9drZyrkWap-x30rO-kVUcM");
//We can make a copy of the template, name it, and optionally tell it what folder to live in
//file.makeCopy will return a Google Drive file object
var folder = DriveApp.getFolderById("1MeU3-N3BMqOPvoaSGr2XassibR2XajdN")
var copy = file.makeCopy(roomorarea + '_' + timestamp, folder);
//Once we've got the new file created, we need to open it as a document by using its ID
var doc = DocumentApp.openById(copy.getId());
//Since everything we need to change is in the body, we need to get that
var body = doc.getBody();
//Then we call all of our replaceText methods
body.replaceText('{{location}}', location);
body.replaceText('{{room}}', roomorarea);
body.replaceText('{{completedby}}', who);
body.replaceText('{{checkedby}}', checkname);
body.replaceText('{{checkeddate}}', checkeddate);
body.replaceText('{{insdate}}', dateofworks);
body.replaceText('{{Empcon}}', employeeorcon);
body.replaceText('{{Type}}', type);
body.replaceText('{{image}}', imageofcompletedcon);
body.replaceText('{{methchk}}', checkedoffintext);
body.replaceText('{{checker}}', checkname);
body.replaceText('{{cocheck}}', checkco);
body.replaceText('{{datecheck}}', checkeddate);
//Lastly we save and close the document to persist our changes
doc.saveAndClose();
}
New to this, but tried insertimage etc, but really a bit beyond me.

Create PDF from Google Sheet Template

I am fairly new to code and App Script, but I've managed to come up with this from research.
Form submitted, Sheet populated, take entry data, copy and append new file, save as pdf, email pdf
I've created examples of what I've been trying to do
Link to form - https://docs.google.com/forms/d/e/1FAIpQLSfjkSBkn3eQ1PbPoq0lmVbm-Dk2u2TP_F_U5lb45SddsTsgsA/viewform?usp=sf_link
link to spreadsheet - https://docs.google.com/spreadsheets/d/1kWQCbNuisZsgWLk3rh6_Iq107HoK7g-qG2Gln5pmYTE/edit?resourcekey#gid=1468928415
link to template - https://docs.google.com/spreadsheets/d/1Ye7DyJQOjA3J_EUOQteWcuASBCfqlA-_lzyNw0REjY8/edit?usp=sharing
However I receive the following error - Exception: Document is missing (perhaps it was deleted, or you don't have read access?)
at Create_PDF(Code:32:34)
at After_Submit(Code:13:21)
App Script Code as follows - If I use a google Doc as a template it works. However I would like to use a spreadsheet as a template, and have the result pdf content fit to page. Please let me know if you need any additional information for this to work.
function After_Submit(e, ){
var range = e.range;
var row = range.getRow(); //get the row of newly added form data
var sheet = range.getSheet(); //get the Sheet
var headers = sheet.getRange(1, 1, 1,5).getValues().flat(); //get the header names from A-O
var data = sheet.getRange(row, 1, 1, headers.length).getValues(); //get the values of newly added form data + formulated values
var values = {}; // create an object
for( var i = 0; i < headers.length; i++ ){
values[headers[i]] = data[0][i]; //add elements to values object and use headers as key
}
Logger.log(values);
const pdfFile = Create_PDF(values);
sendEmail(e.namedValues['Your Email'][0],pdfFile);
}
function sendEmail(email,pdfFile,){
GmailApp.sendEmail(email, "Subject", "Message", {
attachments: [pdfFile],
name: "From Someone"
});
}
function Create_PDF(values,) {
const PDF_folder = DriveApp.getFolderById("1t_BYHO8CqmKxVIucap_LlE0MhslpT7BO");
const TEMP_FOLDER = DriveApp.getFolderById("1TNeI1HaSwsloOI4KnIfybbWR4u753vVd");
const PDF_Template = DriveApp.getFileById('1Ye7DyJQOjA3J_EUOQteWcuASBCfqlA-_lzyNw0REjY8');
const newTempFile = PDF_Template.makeCopy(TEMP_FOLDER);
const OpenDoc = DocumentApp.openById(newTempFile.getId());
const body = OpenDoc.getBody();
for (const key in values) {
body.replaceText("{{"+key+"}}", values[key]);
}
OpenDoc.saveAndClose();
const BLOBPDF = newTempFile.getAs(MimeType.PDF);
const pdfFile = PDF_folder.createFile(BLOBPDF);
console.log("The file has been created ");
return pdfFile;
}
You get the error message with Google Sheets because you are using a Google Doc class to create the PDF, which is not compatible with Google Sheets.
DocumentApp can only be used with Google Docs. I will advise you to change
const OpenDoc = DocumentApp.openById(newTempFile.getId());
for
const openDoc = SpreadsheetApp.openById(newTempFile.getId());
const newOpenDoc = openDoc.getSheetByName("Sheet1");
And depending on the Google Sheet where the "Body" of the information is located. Replace:
const body = OpenDoc.getBody();
for an equivalent like getRange() or any Range class that helps you target the information you need. For example:
// This example is assuming that the information is on the cel A1.
const body = newOpenDoc.getRange(1,1).getValue();
The template for the PDF should be something like this:

How do I get the image ID of an image IN THE CELL?

I'm currently working on a code in Google Apps Script that allows a user to fill out a spreadsheet and have the spreadsheet generate printouts for a job board. I'm trying to design this in a way where the user can simply insert a logo image into a row of my Google sheet and have it replace a placeholder in my doc template.
I have found lots of answers about how you can take an image and convert it to a blob and insert it from a url or an ID, however, I can't seem to find a way to get the ID or url from the image in the cell.
Here's my code currently:
//Creates menu option on spreadsheet
function onOpen() {
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('AutoFill Docs');
menu.addItem('Create New Docs', 'createNewGoogleDocs');
menu.addToUi();
}
//Defines where to get template and info from
function createNewGoogleDocs() {
const googleDocTemplate = DriveApp.getFileById('14MJNd37pn6D-EmNKCQzXXvxJCcOAoB3KS-TlDgZuWMI');
const destinationFolder = DriveApp.getFolderById('120Sb_CJJlmz5NzJW8W3DB4TNuC4kdD3e');
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('JobBoard');
const rows = sheet.getDataRange().getValues();
rows.forEach(function(row, index) {
if (index === 0) return;
if (row[9]) return;
const copy = googleDocTemplate.makeCopy(`${row[1]}, ${row[0]} Printout`, destinationFolder);
const doc = DocumentApp.openById(copy.getId())
const body = doc.getBody();
const friendlyDate = new Date(row[2]).toLocaleDateString();
//Replacing text
body.replaceText('{{Company}}', row[1]);
body.replaceText('{{jobTitle}}', row[0]);
body.replaceText('{{datePosted}}', friendlyDate);
body.replaceText('{{Description}}', row[3]);
body.replaceText('{{Qualifications}}', row[5]);
body.replaceText('{{Wage}}', row[4]);
body.replaceText('{{Apply}}', row[6]);
//A subfunction to handle replacing the image
function textToImage() {
var replaceTextToImage = function(body, searchText, image, width) {
var next = body.findText(searchText);
if (!next) return;
var r = next.getElement();
r.asText().setText("");
var img = r.getParent().asParagraph().insertInlineImage(0, image);
if (width && typeof width == 100) {
var w = img.getWidth();
var h = img.getHeight();
img.setWidth(width);
img.setHeight(width * h / w);
}
return next;
};
var documentId = doc;
var replaceText = "{{Upload Image}}";
var imageFileId = "### File ID of image ###"; //I don't know how to get this variable
var body = DocumentApp.openById(documentId).getBody();
var image = DriveApp.getFileById(imageFileId).getBlob();
do {
var next = replaceTextToImage(body, replaceText, image, 200);
} while (next);
}
//Close and saves new doc
doc.saveAndClose();
const url = doc.getUrl();
sheet.getRange(index + 1, 10).setValue(url)
})
}
I think what might be messing me up is that I have to loop through all my cells right now so that I can create multiple documents at once (meaning each row will have a different doc and different image ID). I'm just not sure how to work around that.
Here's the template and spreadsheet
https://docs.google.com/spreadsheets/d/1cySHogAxcUgzr0hsJoTyPZakKQkM6uIOtmyPzcMoJUM/edit?usp=sharing
https://docs.google.com/document/d/14MJNd37pn6D-EmNKCQzXXvxJCcOAoB3KS-TlDgZuWMI/edit?usp=sharing
There is a bit of an issue trying to get an image in a specific cell. There's even a Feature Request for that. This year Google released a few classes for image management but there seems to be issues when retrieving those using cellImage class.
I found a related answer (workaround) from user #Tanaike where images are retrieved from Google Sheets, converted to a Blob and inserted into a Google Doc.
Sample code provided was:
const spreadsheetId = "###"; // Google Spreadsheet ID
const res = DocsServiceApp.openBySpreadsheetId(spreadsheetId).getSheetByName("Sheet1").getImages();
console.log(res); // You can check the retrieved images at the log.
if (res.length == 0) return;
const blob = res[0].image.blob; // Here, 1st image of Sheet1 is retrieved. Of course, you can choose the image on the sheet.
let doc = DocumentApp.create("newDocName Goes_Here");
var body = doc.getBody();
var imgPDF = body.appendImage(blob);
Take into consideration that to make the above work you need to:
Install Google Apps Script library. (instructions here)
Enable Drive API.
I tested this and indeed, got the images from the given sheet and inserted them into the Google Doc specified. For some reason, running your code did not show me a newly created file from the template but you can tweak the above accordingly to your case.

Pushing a simple Log string from a Google Ad Script to a Google Sheet

I am trying to set up a script which can push data from an App Script into a Google Sheet.
I have the script successfully logging what I want, which goes in the following format Account budget is 12344, but now I want to push this into a Google Sheet. I have set up a variable containing the URL and another variable containing the sheet name, and also a clear method to delete anything already there.
Find the code I have below:
// - The link to the URL
var SPREADSHEET_URL = 'abcdefghijkl'
// - The name of the sheet to write the data
var SHEET_NAME = 'Google';
// No to be changed
function main() {
var spreadsheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
var sheet = spreadsheet.getSheetByName(SHEET_NAME);
sheet.clearContents();
}
function getActiveBudgetOrder() {
// There will only be one active budget order at any given time.
var budgetOrderIterator = AdsApp.budgetOrders()
.withCondition('status="ACTIVE"')
.get();
while (budgetOrderIterator.hasNext()) {
var budgetOrder = budgetOrderIterator.next();
Logger.log("Budget Order Amount " + budgetOrder.getSpendingLimit());
}
}
Assuming you want to clear the entire Sheet every time you extract the data this should work for you. You will need to set the url and shtName variables.
function getActiveBudgetOrder() {
var url = 'https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxxxxxxxxxx/';
var shtName = 'Sheet1';
var arr = [];
var sht = SpreadsheetApp.openByUrl(url).getSheetByName(shtName);
// There will only be one active budget order at any given time.
var budgetOrderIterator = AdsApp.budgetOrders()
.withCondition('status="ACTIVE"')
.get();
while (budgetOrderIterator.hasNext()) {
var budgetOrder = budgetOrderIterator.next();
arr.push(["Budget Order Amount " + budgetOrder.getSpendingLimit()]);
}
sht.clearContents();
sht.getRange(1, 1, arr.length, arr[0].length).setValues(arr);
}

copy spreadsheet to a specific folder and remove viewers from the copy

I'm trying to make a copy of the active spreadsheet to a specific folder, then remove all editors from the copied spreadsheet and keep myself as Owner.
Here is my code :
//Save Spreadsheet in selected folder
function freezeSS(vname,option) {
if (vname != ""){
var mainSs = SpreadsheetApp.getActiveSpreadsheet();
var SSID = mainSs.getId();
var CopyDate = Utilities.formatDate(new Date(), "GMT-3", "dd/MM/yy-HH:mm"); // Function Date + Format
var file = DriveApp.getFileById(SSID);
if (option == "Public") {
var folder = DriveApp.getFolderById("0B_EpGZ420rEUfk1mZ2Z3RmFKUk9xaGd1bm1CdGhmZ0FCbTdVT2p2MUlJY3NZUTV0MTR0LTQ");
file.makeCopy(vname + "_" + CopyDate, folder);
}
else {
var folder = DriveApp.getFolderById("0B_EpGZ420rEUfk9jTl81NXNVOXNhRDF2N2R4c1FGTW9wQTB5Q3dNS25nd1NEejBTWWd1RFk");
var backup = file.makeCopy(vname + "_" + CopyDate, folder);
var editors = backup.getEditors().getEmail();
Logger.log(editors);
var permision = backup.removeEditor(editors);
}
} return false;
}
Everything works but I have a problem in removing editors. I always get an error and still have the same editors as in the original spreadsheet.
As the documentation states, you have 2 options for removeEditor():
removeEditor(user)
or
removeEditor(emailAddress)
You're using neither, you'll need to make a loop in the getEditors() without the getEmail(), this one will also get into the loop, as so:
var editors = backup.getEditors();
for( i in editors ){
var email = editors[i].getEmail();
var permision = backup.removeEditor(email);
}
or shorter:
var editors = backup.getEditors();
for( i in editors )
var permision = backup.removeEditor(editors[i]);
BTW, this was well documented, a ctrl+c ctrl+v from there would almost solve your problem, please take a little more time to fiddle with it.
https://developers.google.com/apps-script/reference/drive/user#getEmail();