Script To Write Auto-Gen'd Google Doc URL To Spreadsheet - google-apps-script

I have a Google Form that does two things upon hitting the Submit button. First, it dumps that data into a Spreadsheet, then it autofills a Google Doc Template with the info from the Form.
In my script to autofill the Google Doc, I've grabbed the URL for the Google Doc. But I need to write this URL into the last row of Column J in my Google Sheet. Correction: using the getActiveSheet functions are fine, I forgot this script is running from the (Active) Google Sheet (apologies!).
Can anyone assist with this? Here's a snippet of the script to get the URL:
function autoFillGoogleDocFromForm(e) {
//e.values is an array of form values
var TimeStamp = e.values[0];
var Technician = e.values[1];
var Vendor = e.values[2];
var xxx = e.values[3];
var yyy = e.values[4];
var SerialNumber = e.values[5];
var AssetTag = e.values[6];
var TicketNumber = e.values[7];
var HostName = e.values[8];
var DocumentLink = e.values[9];
var Return = e.values[10];
var Platform = e.values[11];
var Summary = e.values[12];
var URL = "";
//file is the template file, and you get it by ID
var file = DriveApp.getFileById('aaa');
//Put auto filled Google Doc into the appropriate Vendor Folder
//file.makeCopy will return a Google Drive file object
if (Vendor == "111") {
var folder = DriveApp.getFolderById('bbb')
var copy = file.makeCopy(TicketNumber + ' - ' + SerialNumber, folder);
}
if (Vendor == "222") {
var folder = DriveApp.getFolderById('ccc')
var copy = file.makeCopy(TicketNumber + ' - ' + SerialNumber, folder);
}
if (Vendor == "333") {
var folder = DriveApp.getFolderById('ddd')
var copy = file.makeCopy(TicketNumber + ' - ' + SerialNumber, 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());
//Get the url of the newly created Google Doc
var url = doc.getUrl();
//Script to write this URL into the Shared Google Sheet will go here
//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('{{EmailAddress}}', Technician);
body.replaceText('{{TicketNumber}}', TicketNumber);
body.replaceText('{{HostName}}', HostName);
body.replaceText('{{SerialNumber}}', SerialNumber);
body.replaceText('{{AssetTag}}', AssetTag);
body.replaceText('{{Summary}}', Summary);
body.replaceText('{{Vendor}}', Vendor);
body.replaceText('{{URL}}', url);
//Lastly we save and close the document to persist our changes
doc.saveAndClose();
Thanks in advance!

From your following situation in your question,
Also this is a shared Google Sheet, so I can't use the getActiveSheet function, I'd need to reference the Google Sheet URL, I believe.
If you have the permission to write the values to the shared Google Spreadsheet, how about the following modification?
From:
var url = doc.getUrl();
//Script to write this URL into the Shared Google Sheet will go here
To:
var url = doc.getUrl();
//Script to write this URL into the Shared Google Sheet will go here
var sheet = SpreadsheetApp.openById("### Spreadsheet ID ###").getSheetByName("### sheet name ###");
sheet.appendRow([url]);
In this modification, please set the Spredsheet ID and sheet name. By this, the value of url is appended to the next row of the last row of the sheet in the Google Spreadsheet.
If you want to use the Spreadsheet URL instead of Spreadsheet ID, please modify openById("### Spreadsheet ID ###") to openByUrl("### Spreadsheet URL ###").
References:
openById(id)
openByUrl(url)
appendRow(rowContents)
Edit:
From the following replying,
That doesn't seem to work to insert the URL into the last row of column J unfortunately. It looks like this script can use the Active Sheet afterall, I'll edit the OP.
In this case, how about the following modification?
From:
var url = doc.getUrl();
//Script to write this URL into the Shared Google Sheet will go here
To:
var url = doc.getUrl();
//Script to write this URL into the Shared Google Sheet will go here
// var sheet = SpreadsheetApp.openById("### Spreadsheet ID ###").getSheetByName("### sheet name ###");
var sheet = SpreadsheetApp.getActiveSheet(); // or SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheetname")
sheet.getRange("J" + (sheet.getLastRow() + 1)).setValue(url);
Or
var url = doc.getUrl();
//Script to write this URL into the Shared Google Sheet will go here
// This is from https://stackoverflow.com/a/44563639/7108653
Object.prototype.get1stEmptyRowFromTop = function (columnNumber, offsetRow = 1) {
const range = this.getRange(offsetRow, columnNumber, 2);
const values = range.getDisplayValues();
if (values[0][0] && values[1][0]) {
return range.getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow() + 1;
} else if (values[0][0] && !values[1][0]) {
return offsetRow + 1;
}
return offsetRow;
};
// var sheet = SpreadsheetApp.openById("### Spreadsheet ID ###").getSheetByName("### sheet name ###");
var sheet = SpreadsheetApp.getActiveSheet(); // or SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheetname")
sheet.getRange("J" + sheet.get1stEmptyRowFromTop(10)).setValue(url);

Suggestion:
If you need to write the URL value into the last row of Column J in a shared Google Spreadsheet file, you can try this sample below:
function sample() {
var ss = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/SHEET_ID_IS_HERE/edit#gid=0');
var sheet = ss.getSheets()[0]; // access first sheet
var activeSheet = ss.setActiveSheet(sheet);
//Sample setValue() action to the shared sheet file
activeSheet.getRange("A1").setValue("URL");
}
NOTE: You would need to have an editor permission on the Shared Google Sheet file that you're accessing
References:
openByUrl(url)
getSheets()
setActiveSheet(sheet)

Here's the code that ended up working for me.
//Get the url of the newly created Google Doc
var url = doc.getUrl();
// var sheet = SpreadsheetApp.openById("### Spreadsheet ID ###").getSheetByName("### sheet name ###");
var sheet = SpreadsheetApp.getActiveSheet(); // or SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheetname")
sheet.getRange("J" + (sheet.getLastRow())).setValue(url);

Related

How to Rename Form Response Destination Sheet with Google App Script

I have script to generate Google Form and set where the Destination Form Response. After set, I want to rename the Sheet Name, from default Response Sheet Name ( eg. Form Response 1), with custom Sheet Name.
function genarateForm() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName('CONSUMEN DATA');
var rowNumber = (sheet.getDataRange().getNumRows())-3;
var myQuestions = sheet.getRange(5,2,rowNumber,1).getValues();
var form = FormApp.create('CUSTOMER BILL');
const formTitle = 'BILL REPORT'
form.setTitle(formTitle)
.setDescription('Fill with customer bill :')
.setAllowResponseEdits(true)
.setAcceptingResponses(true);
for(var i=0;i<rowNumber;i++){
var addItem = form.addTextItem();
addItem.setTitle(pertanyaanKu[i]);
}
form.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId())
// I want to set the sheet destination response with "CUSTOMER BILL"
}
In order to achieve your goal, how about the following modification?
From:
form.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId())
To:
form.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId());
// I added the below script.
SpreadsheetApp.flush(); // This might not be required to be used.
const formUrl = form.getEditUrl().replace("edit", "viewform");
const formSheet = ss.getSheets().find(s => s.getFormUrl() == formUrl);
if (formSheet) {
formSheet.setName("CUSTOMER BILL");
}
When this script is run, the sheet name of a sheet of the created Google Form is changed to "CUSTOMER BILL".

Exception: Invalid argument for variable in Google Apps Script

I am trying to link a Google Sheets file with a Google Doc file and replace the text of the Google Docs with some custom items.
Exception: Invalid argument: sheet
autoFillGoogleDocFromForm # Code.gs:5
This error is generated by the following code. I used (name of sheet), (file id inserted), (folder id inserted) instead of showing the actual values.
function autoFillGoogleDocFromForm(e) {
var activateSheet = SpreadsheetApp.getActiveSpreadsheet();
SpreadsheetApp.setActiveSheet(activateSheet.getSheetByName('(name of sheet)'));
var sheet = SpreadsheetApp.getActiveSheet();
var row = e.range.getRowIndex();
var timestamp = sheet.getRange(row, 1).getValues();
var file = DriveApp.getFileById('(file id inserted)');
var folder = DriveApp.getFolderById('(folder id inserted)');
var copy = file.makeCopy('' + timestamp, folder);
var doc = DocumentApp.openById(copy.getId());
var header = doc.getHeader();
header.replaceText('{{TIMESTAMP}}', timestamp);
doc.saveAndClose();
}
You can simplify the way you define a Sheet:
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getSheetByName('name of the sheet')
Even if you are not going to use the Spreadsheet object any more, you can directly define the Sheet object
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('name of the sheet')
Make sure you write the name of the sheet properly.

Create backup of spreadsheet with values and format only in specific subfolder

I have been trying to combine two scripts to create a script that i can run on demand by using a custom menu. I would like the script to do the following:
Make a copy with values and format only (no formulas and importrange), of a existing spreadsheet.
Name the copy the same as the original spreadsheet + timestamp in "yyyy-mm-dd" format.
Place the copy in a specific subfolder.
Also to be able to create the copy on demand, using a custom menu button to do so.
And lastly to have a custom menu button for easy access to said subfolder.
So far i have found two scripts that can do parts of this task but have not been able to combine them to a single script. Is this possible?
See the code below for the two scripts:
SCRIPT 1
// Program: Archive GSheet with Date Stamp
// Programmer: Michael Fryar
// Date: 19 September 2017
// Google Apps Script to copy Google Sheet to subfolder with date stamp in name
// Add Custom Menu
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Archive')
.addItem('Archive a copy with timestamp', 'archiveCopy')
.addItem('Open folder', 'openArchive')
.addToUi();
}
// Define function to copy sheet to subfolder with date stamp in name
// Building on https://gist.github.com/abhijeetchopra/99a11fb6016a70287112
function archiveCopy() {
// Replace "spreadsheetId" with the ID of the Google Sheet you wish to copy
var file = DriveApp.getFileById("ID")
// Replace "folderId" with the ID of the folder where you want the copy saved
var destination = DriveApp.getFolderById("ID");
// Get timezone for datestamp
var timeZone = Session.getScriptTimeZone();
// Generate datestamp and store in variable formattedDate as year-month-date
var formattedDate = Utilities.formatDate(new Date(), timeZone , "yyyy-MM-dd");
// Replace "file_name" with the name you want to give the copy
var name = formattedDate + " Copy";
// Archive copy of "file" with "name" at the "destination"
file.makeCopy(name, destination);
}
// Define function to open archive folder in new tab
// Building on https://www.youtube.com/watch?v=2y7Y5hwmPc4
function openArchive() {
// Replace "folderId" with the ID of the folder where you want copies saved
var url = "https://drive.google.com/drive/folders/"
// HTML to open folder url in new tab and then close dialogue window in sheet
var html = "<script>window.open('" + url + "');google.script.host.close();</script>";
// Push HTML into user interface
var userInterface = HtmlService.createHtmlOutput(html);
SpreadsheetApp.getUi().showModalDialog(userInterface, 'Opening Archive Folder');
}
SCRIPT 2
function copyEntireSpreadsheet() {
var id = "ID"; // Please set the source Spreadsheet ID.
var ss = SpreadsheetApp.openById(id);
var srcSheets = ss.getSheets();
var tempSheets = srcSheets.map(function(sheet, i) {
var sheetName = sheet.getSheetName();
var dstSheet = sheet.copyTo(ss).setName(sheetName + "_temp");
var src = dstSheet.getDataRange();
src.copyTo(src, {contentsOnly: true});
return dstSheet;
});
var destination = ss.copy(ss.getName() + " - " + new Date().toLocaleString());
tempSheets.forEach(function(sheet) {ss.deleteSheet(sheet)});
var dstSheets = destination.getSheets();
dstSheets.forEach(function(sheet) {
var sheetName = sheet.getSheetName();
if (sheetName.indexOf("_temp") == -1) {
destination.deleteSheet(sheet);
} else {
sheet.setName(sheetName.slice(0, -5));
}
});
}
UPDATE!
I found another script that could do the parts that i needed and was finally able to combine them together. Posting script below to benefit others in search of something similar:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Archive')
.addItem('Archive copy with timestamp', 'Archive')
.addItem('Open folder', 'openArchive')
.addToUi();
}
function Archive() {
var spreadsheetId = "###"; // Please set the source Spreadsheet ID.
var destFolderId = "###"; // Please set the destination folder ID.
// Copy each sheet in the source Spreadsheet by removing the formulas as the temporal sheets.
var ss = SpreadsheetApp.openById(spreadsheetId);
var tempSheets = ss.getSheets().map(function(sheet) {
var dstSheet = sheet.copyTo(ss).setName(sheet.getSheetName() + "_temp");
var src = dstSheet.getDataRange();
src.copyTo(src, {contentsOnly: true});
return dstSheet;
});
// Get timezone for datestamp
var timeZone = Session.getScriptTimeZone();
// Generate datestamp and store in variable formattedDate as year-month-date
var formattedDate = Utilities.formatDate(new Date(), timeZone , "yyyy-MM-dd");
// Copy the source Spreadsheet.
var destination = ss.copy(ss.getName() + " " + formattedDate);
// Delete the temporal sheets in the source Spreadsheet.
tempSheets.forEach(function(sheet) {ss.deleteSheet(sheet)});
// Delete the original sheets from the copied Spreadsheet and rename the copied sheets.
destination.getSheets().forEach(function(sheet) {
var sheetName = sheet.getSheetName();
if (sheetName.indexOf("_temp") == -1) {
destination.deleteSheet(sheet);
} else {
sheet.setName(sheetName.slice(0, -5));
}
});
// Move file to the destination folder.
var file = DriveApp.getFileById(destination.getId());
DriveApp.getFolderById(destFolderId).addFile(file);
file.getParents().next().removeFile(file);
}
// Define function to open archive folder in new tab
// Building on https://www.youtube.com/watch?v=2y7Y5hwmPc4
function openArchive() {
// Replace "folderId" with the ID of the folder where you want copies saved
var url = "https://drive.google.com/drive/folders/folderid"
// HTML to open folder url in new tab and then close dialogue window in sheet
var html = "<script>window.open('" + url + "');google.script.host.close();</script>";
// Push HTML into user interface
var userInterface = HtmlService.createHtmlOutput(html);
SpreadsheetApp.getUi().showModalDialog(userInterface, 'Opening Archive Folder');
}

Trying to delete named external sheets from a container script

My first time scripting, I'm trying to create an order sheet for outside sales reps, that they can fill out, and click a button to submit a template file. Sheets 0 & 1 are survey questions that fill in references on sheet 2 (Final Product). Sheets 3 & 4 are data validation fields for sheets 0 & 1 including email addresses for recipients.
I am trying to copy a spreadsheet to a new spreadsheet, --> converting a specific sheet from the copy to text so that I can delete all sheets except the converted sheet and not get reference errors --> email converted sheet via pdf format to contacts on sheet 3. My code does it all except delete the 4 sheets that I want to(0,1,3,4). The script is a container script, so whenever I try to call SpreadsheetApp.getActiveSpreadsheet(); It automatically grabs the container file and deletes the 'template' sheets. I need to know how to delete indexes 0,1,3,4 of an external spreadsheet, in a different folder of my drive.
The code below is a hodgepodge of snippets I have butchered and pieced together.
I hope you can understand all of this.
Here is what I have:
function SubmitOnClicks() {
// Set the Active Spreadsheet so we don't forget
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var orderSheet = ss.getSheetByName("Order Sheet");
var ValidationRef = ss.getSheetByName("Validation References");
orderSheet.activate();
// Set the message to attach to the email.
var message = "Please see attached";
// Get Project Name from Range B3:D3
var projectname = ss.getRange("B3").getValues();
// Get BFS Size from Range C25:E25
var size = ss.getRange("C25:E25").getValues();
// Construct the Subject Line
var subject = projectname + " " + size;
// Get contact details from "Validation References" sheet and construct To: Field
var numRows = ValidationRef.getLastRow();
var emailTo = ValidationRef.getRange(2, 12, 5, 2).getValues();
// Google scripts can't export just one Sheet that I know of
var submittalDate = orderSheet.getRange(17, 2).getValues();
var submittalName = "BFS Submittal"
var folderID = "My Drive Folder ID"; // Folder id to save Copy to: MyDrive/_Sheets/Shared/BFS Exports
var folder = DriveApp.getFolderById(folderID);
var sourceSheet = ss.getSheetByName("Order Sheet");
var sourceRange = sourceSheet.getRange(1,1,sourceSheet.getMaxRows(),sourceSheet.getMaxColumns());
var sourcevalues = sourceRange.getValues();
var destSpreadsheet = SpreadsheetApp.open(DriveApp.getFileById(ss.getId()).makeCopy(submittalName, folder))
var destSheet = destSpreadsheet.getSheets()[2];
var destRange = destSheet.getRange(1, 1, destSheet.getMaxRows(), destSheet.getMaxColumns());
// Replace cell values with text (to avoid broken references)
destRange.setValues(sourcevalues);
var files = DriveApp.searchFiles(
'mimeType = "BFS Submittal' + MimeType.GOOGLE_SHEETS + '"');
while (files.hasNext()) {
var spreadsheet = SpreadsheetApp.open(files.next()); //I'm stuck after this Line
var sheet = spreadsheet.getSheets()[0, 1, 3, 4];
}
// Make the PDF, currently called "BFS Submittal.pdf"
var pdf = DriveApp.getFileById(ss.getId()).getAs('application/pdf').getBytes();
var attach = {fileName:'BFS Submittal',content:pdf, mimeType:'application/pdf'};
// Send the freshly constructed email
MailApp.sendEmail(emailTo, subject, message, {attachments:[attach]});
}

Downloading a PDF version of the open spreadsheet in Google Drive via scripts

I've been reading up on how to save a spreadsheet to PDF via Google Docs Scripting. Most suggestions I've come across reference using something like:
theOutputFile.saveAndClose();
DocsList.createFile(theOutputFile.getAs('application/pdf')).rename(theOutputName+".pdf");
That is, they reference the saveAndClose() function. I don't want to save or close my spreadsheet - but I do want to download the current sheet as a PDF.
Any suggestions? Thanks.
For saving the current sheet as a PDF, you can hide all the other sheets, save the current, & then show all sheets again.
The pdf creation might start before the end of the sheets' hiding and then will include 2 sheets - the current & the last sheets - in the pdf file.
Adding a sleep or a confirmation msgbox, between showOneSheet & createPdf eliminated the problem.
This answer is a variation of Marco Zoqui's answer: "To send a single sheet you may hide all other before sending" in Google Apps Script to Email Active Spreadsheet
var sheet = SpreadsheetApp.getActiveSheet();
var sheetToSave = sheet.getName();
showOneSheet(sheetToSave);
Utilities.sleep(2000);
createPdf("TestFolder", "TestPDF");
showAllSheets();
function showOneSheet(SheetToShow) {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for(var i in sheets){
if (sheets[i].getName()==SheetToShow){
sheets[i].showSheet();
}
else {
sheets[i].hideSheet();
}
}
}
function showAllSheets() {
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for(var i in sheets){
sheets[i].showSheet();
}
}
function createPdf(saveToFolder, fileName){
var ssa = SpreadsheetApp.getActiveSpreadsheet();
var pdf = ssa.getAs("application/pdf");
try {
var folder = DocsList.getFolder(saveToFolder);
}
//Create Folder if not exists
catch(error){
folder = DocsList.createFolder(saveToFolder);
}
var file = folder.createFile(pdf);
file.rename(fileName);
return file;
}
I was able to get it to work using #hsgv's answer, however, this is the version I ended up using based on this.
// global save to folder variable:
var folderName = "My/Special/Folder";
function createInvoiceInGoogleDrive(){
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
// getting some values from the spreadhseet for the file name
var invoiceNumber = sheet.getRange("E3").getValue();
var vendor = sheet.getRange("A9").getValue();
var fileName = invoiceNumber + ' - ' + vendor + " - Invoice.pdf";
var pdfBlob = sheetToPDF(spreadsheet, sheet);
pdfBlob.setName(fileName);
var folder = getOrCreateFolder(folderName);
var matchingFileList = folder.find(fileName);
if ( matchingFileList.length > 0 ) {
Browser.msgBox("ERROR: New invoice not created. " + fileName + " already exists at " + folderName);
return false;
} else {
var f = folder.createFile(pdfBlob);
spreadsheet.toast('Created a new invoice on Google Drive!');
return true;
}
}
// thanks: https://gist.github.com/gregorynicholas/9008572
function sheetToPDF(spreadsheet, sheet) {
var ssID = spreadsheet.getId();
var gid = sheet.getSheetId();
// &gid=x at the end of above url if you only want a particular sheet
var url2 = "http://spreadsheets.google.com/feeds/download/spreadsheets/Export?key=" + ssID +
"&gid=" + gid +
"&fmcmd=12&size=7&fzr=true&portrait=true&fitw=true&locale=en&gridlines=false&printtitle=false&sheetnames=false&pagenum=UNDEFINED&attachment=true";
// AUTH TOKEN required to access the UrlFetchApp call below. You can receive it
// from https://appscripts.appspot.com/getAuthToken
var AUTH_TOKEN = "{GET YOUR OWN AUTH TOKEN}";
var auth = "AuthSub token=\"" + AUTH_TOKEN + "\"";
var res = UrlFetchApp.fetch(url2, {headers: {Authorization: auth}}).getBlob();
return res;
}
/**
* Get or create a folder based on its name/path
*/
function getOrCreateFolder(folderName) {
try {
var theFolder = DocsList.getFolder(folderName);
} catch(error){
var theFolder = DocsList.createFolder(folderName);
}
return theFolder;