Locate rows with identical values in a column and email each of those rows in 1 email - google-sheets-api

I am trying to write an Apps script for my google sheet.
Problem: I have data with the following column headings: Email; ID; Category; $Amount; Comment. There are multiple rows that have the same ID. I need all rows with the same ID to be sent in an email to the email address associated with that line.
So the email for example could (doesn't have to be) a table with the data enclosed
[sample email][2]
Can anyone help me with this?
Here is some code I wrote in Apps Script trying to do this on my own but it doesn't work:
var Send = "Y";
function sendEmails() {
//specify the sheet and definitions
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var lastrow = sheet.getLastRow();
var startrow = 1; // This is the 1st row of data to process
var numrows = lastrow; //This is the number of rows to process
//This section formats the colums so they appear correctly in the html email below
var column = sheet.getRange("A:A");
column.setNumberFormat("#"); //simple plain text format
var column = sheet.getRange("B:B");
column.setNumberFormat("#"); //simple plain text format
var column = sheet.getRange("D:D");
column.setNumberFormat("#"); //simple plain text format
var column = sheet.getRange("E:E");
column.setNumberFormat("#"); //simple plain text format
var column = sheet.getRange("H:H");
column.setNumberFormat("$0.00"); //simple currency format
var column = sheet.getRange("I:I");
column.setNumberFormat("#"); //simple plain text format
var column = sheet.getRange("J:J");
column.setNumberFormat("#"); //simple plain text format
//This section specifies the actual data we will be working with
var datarange = sheet.getRange(startrow, 1, lastrow, 15) // Fetch the range of cells
var data = datarange.getValues(); // Fetch values for each row in the Range.
//Defining the Column data
for (var i = 0; i < data.length; ++i) {
var col = data[i];
var Name = col[0]; //Column starting at 0 from left to right
var EmailAddress = col[1]; //Column starting at 0 from left to right
var Send = col[2]; //Column starting at 0 from left to right
var LeaseID = col[3]; //Column starting at 0 from left to right
var ExpenseCategory = col[4]; //Column starting at 0 from left to right
var TransactionDate = col[5]; //Column starting at 0 from left to right
var Type = col[6]; //Column starting at 0 from left to right
var Amount = col[7]; //Column starting at 0 from left to right
var PayeeName = col[8]; //Column starting at 0 from left to right
var Comment = col[9]; //Column starting at 0 from left to right
var EmailSent = col[10]; //Column starting at 0 from left to right
var Subject = "xxxxxxx June Payment Detail"; //Suject for the email to be sent
var emailintro = //Introduction part of the email
'Hi' + Name + ',<br /><br />' +
'In order to ensure proper account credit, please see the details from this months payment below: <br /><br />'
var emailtrans = 'List' + ExpenseCategory + Amount + Comment + '<br /><br />'
var emailend = // The end of the email
'Please let us know if you have any questions. You may reach us at xxxxx#xxxxx.com'
var me = Session.getActiveUser().getEmail();
var aliases = GmailApp.getAliases(); //Gets the alias xxxxxx#xxxx.com from account
Logger.log(aliases); //logs the alias
//This section is the one that actually sends the emails
//if (EmailAddress != "" && EmailSent !=""{
if (aliases.length > 0) { // Prevents sending duplicates
GmailApp.sendEmail(EmailAddress, subject, emailintro + emailtrans + emailend, {
'from': aliases[0],
'replyto': 'xxxxxx#xxxxxx.com',
htmlbody: emailintro + emailtrans + emailend
});
sheet.getrange(startrow + i, 392).setVaue(SENT);
SpreadsheetApp.flush(); //Make Sure the cell is updated right away in case the script is interrupted
}
}
}
//}}else {}}}

Related

How do I use variables to avoid having to create a set of these for all of the columns in my sheet?

I'm trying to get my sheet to automatically recalculate a set of dates within a schedule, in both directions, when a cell is changed.
The code works fine, but I need to add a bunch more columns and I'd really rather not copy/paste/find/replace a load more times. I'm fairly certain I can do this with variables (just looking up the column identifier and feeding that into the code somehow), but I don't know-how.
functJon onEdJt(e) {
var sh = e.source.getActJveSheet();
Jf(sh.getName() === 'Date Calculator' && e.range.getA1NotatJon() === 'C9'
)
{
sh.getRange("C10").setFormula("=WORKDAY(C9,+$C$3)");
sh.getRange("C11").setFormula("=WORKDAY(C10,+10)");
sh.getRange("C12").setFormula("=WORKDAY(C11,+$C$4)");
sh.getRange("C13").setFormula("=WORKDAY(C12,+$C$3)");
sh.getRange("C14").setFormula("=WORKDAY(C13,+10)");
sh.getRange("C15").setFormula("=WORKDAY(C14,+1)");
sh.getRange("C16").setFormula("=WORKDAY(C15,+$C$5)");
}
else Jf (sh.getName() === 'Date Calculator' && e.range.getA1NotatJon()
=== 'C10' )
{
sh.getRange("C9").setFormula("=WORKDAY(C10,-$C$3)");
sh.getRange("C11").setFormula("=WORKDAY(C10,+10)");
sh.getRange("C12").setFormula("=WORKDAY(C11,+$C$4)");
sh.getRange("C13").setFormula("=WORKDAY(C12,+$C$3)");
sh.getRange("C14").setFormula("=WORKDAY(C13,+10)");
sh.getRange("C15").setFormula("=WORKDAY(C14,+1)");
sh.getRange("C16").setFormula("=WORKDAY(C15,+$C$5)");
Ideally the code should then just "work" for any number of columns in the sheet, so I don't need to add more code if I add more columns.
Update
Here's an example of what I'm trying (but it's not working) - attempting to check that the active cell is in row 9 of a specific column before then running the "set.Formula" functions:
function onEdit(e) {
var sh = e.source.getActiveSheet();
var col = e.source.getActiveSheet().getRange().getColumn();
var row = e.source.getActiveSheet().getRange().getRow();
if(sh.getName() === 'Date Calculator' && e.getRange('9',col) )
Event Objects
Even though the code was written as onEdit(e), you didn't take advantage of the Event Objects.
In this answer, the code returns the new value of the edited cell and also the range. The range is then used to work out the row, column and sheet name and these is used for validation as well as for building the ranges and the setFormula
Variables
The code includes variables for the valid range of columns that can be used for data entry (Column C to Column H), and respective input rows (rows 9 and 10). These are expressed as values, but they could just as easily be written into the spreadsheet as assumptions and the values obtained in the code by using getValue.
The absolute cell references used in the setFormula are partly variable (column reference) and part hard-coded (the respective rows-3,4 and 5). If desired, the rows could be variable as well.
Efficiency
There is just one if statement containing one version of the code to build setFormula.
This is achieved by designing the if statement:
1. if the sheet = "Date Calculator" AND
2. if the editColumn is between the valid ColumnStart and ColumnEnd values (Column C to H) AND
3. if the editRow is between the valid Row values (rows 9 or 10) AND
4. if the edited value isn't a blank (length != 0).
The last condition ("edited value is blank") ensures that if cell contents are been deleted (and/or have no value), then the code won't proceed.
Convert column number to letter
I used a routine written by #AdamL found at Convert column index into corresponding column letter; this converts a column number into a letter. It's used to build the "targetcolumn" address in Workdays. It's valid for the letters A-Z; there's a version for letters beyond Z.
Cleanup
If data is entered into row 10 of a given column, then any value in row 9 (of the same column) needs to be deleted. The code does this and also deletes any pre-existing formula dates in the rows below so there is no confusion about the dates derived by the data entry.
function onEdit(e){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetname = "Date Calculator";
var sheet = ss.getSheetByName(sheetname);
// get the event source data
var editedCell = e.range;
var editRow = editedCell.getRow();
var editCol = editedCell.getColumn();
var eValue = e.value;
var editedSheet = editedCell.getSheet().getName();
//Logger.log("DEBUG: the cell = "+editedCell.getA1Notation()+", the column = "+editCol+", the row is "+editRow+", the value is "+eValue+", the edited sheet is "+editedSheet);
// create some variables for column and row range
var columnStart = 3; // Column C
var columnEnd = 8; // Column H
var rowOption1 = 9; // row 9
var rowOption2 = 10 // row 10
// create some variables for target cells
var absolutecolumn = "C";
//var absoluterow1 = 3; // not used
//var absoluterow2 = 4; // not used
//var absoluterow3 = 5; // not used
// test for valid edit in row option 1 // Row 9
if(editedSheet === sheetname && columnEnd >=editCol && editCol>=columnStart && rowOption2>=editRow && editRow>=rowOption1 && eValue.length !=0 ){
//Logger.log("DEBUG: You got the right sheet, the edit is in the right range of columns and the edited row was = "+rowOption1);
if (editRow == rowOption2){
// clear row 9
sheet.getRange((+editRow-1),editCol).clear();
}
// clear following 8 rows of data
sheet.getRange((+editRow+1),editCol,8).clear();
// set the targetcolumn as a letter
var targetcolumn = columnToLetter(editCol);
// set formula for row+1
sheet.getRange((+editRow+1),editCol).setFormula("=WORKDAY("+targetcolumn+editRow+",$"+absolutecolumn+"$3)"); //
// set formula row +2
sheet.getRange((+editRow+2),editCol).setFormula("=WORKDAY("+targetcolumn+(+editRow+1)+",+10)");
// set formula row +3
sheet.getRange((+editRow+3),editCol).setFormula("=WORKDAY("+targetcolumn+(+editRow+2)+",$"+absolutecolumn+"$4)");
// set formula row +4
sheet.getRange((+editRow+4),editCol).setFormula("=WORKDAY("+targetcolumn+(+editRow+3)+",$"+absolutecolumn+"$3)");
// set formula row + 5
sheet.getRange((+editRow+5),editCol).setFormula("=WORKDAY("+targetcolumn+(+editRow+4)+",+10)");
// set formula row + 6
sheet.getRange((+editRow+6),editCol).setFormula("=WORKDAY("+targetcolumn+(+editRow+5)+",+1)");
// set formula row + 7
sheet.getRange((+editRow+7),editCol).setFormula("=WORKDAY("+targetcolumn+(+editRow+6)+",$"+absolutecolumn+"$5)");
// change the background to show entry in rowoption1
sheet.getRange(editRow,editCol).setBackground("yellow");
sheet.getRange((+editRow+1),editCol).setBackground("white");
}
}
function columnToLetter(column)
{
var temp, letter = '';
while (column > 0)
{
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = (column - temp - 1) / 26;
}
return letter;
}
Screenshot

Merging many spreadsheets into report file exceeds maximum execution time

I am using the following script to add rows of files from a student loop in the Google spreadsheet if credits are less than x. The script was working good but as the data in the spreadsheet is being added daily, now the script is throwing "Exceeded maximum execution time" error (we have more than 2000 files). As I am new to scripting I don't know how to optimize the code.
Could someone help me to optimize the code or any solution so that the execution time take less than 5 min. Every time you compare to an email, it has to be compared to many emails. Please Help!
function updated() {
//Final file data (Combined)
var filecombined = SpreadsheetApp.openById("XXXXXXXXXX");
var sheet2 = filecombined.getSheets();
//Folder with all the files
var parentFolder = DriveApp.getFolderById("YYYYYYYYYYYY");
var files = parentFolder.getFiles();
//Current Date
var fecha = new Date();
//Path for each file in the folder
while (files.hasNext()) {
var idarchivo = files.next().getId();
var sps = SpreadsheetApp.openById(idarchivo);
var sheet = sps.getSheetByName('STUDENT PROFILE');
var data = sheet.getDataRange().getValues();
var credits = data[5][1];
//Flat; bandera:1 (new row), bandera:2 (update row)
var bandera = 1;
//Take data from final file (Combined)
var data2 = sheet2[0].getDataRange().getValues();
//If credits are less than X: write
if (credits < 120) {
var email = data[2][1];
var lastrow = filecombined.getLastRow();
var u = 0;
//comparison loop by email, if found it, update and exit the loop
while (u < lastrow) {
u = u + 1;
if (email == data2[u - 1][1]) {
sheet2[0].getRange(u, 3).setValue(credits);
sheet2[0].getRange(u, 4).setValue(fecha);
u = lastrow;
bandera = 2;
}
}
//if that email does not exist, write a new row
if (bandera == 1) {
var nombre = data[0][1];
sheet2[0].getRange(lastrow + 1, 1).setValue(nombre);
sheet2[0].getRange(lastrow + 1, 2).setValue(email);
sheet2[0].getRange(lastrow + 1, 3).setValue(credits);
sheet2[0].getRange(lastrow + 1, 4).setValue(fecha);
}
}
}
SpreadsheetApp.flush();
}
The questioner's code is taking taking more than 4-6 minutes to run and is getting an error Exceeded maximum execution time.
The following answer is based solely on the code provided by the questioner. We don't have any information about the 'filecombined' spreadsheet, its size and triggers. We are also in the dark about the various student spreadsheets, their size, etc, except that we know that there are 2,000 of these files. We don't know how often this routine is run, nor how many students have credits <120.
getvalues and setvalues statements are very costly; typically 0.2 seconds each. The questioners code includes a variety of such statements - some are unavoidable but others are not.
In looking at optimising this code, I made two major changes.
1 - I moved line 27 var data2 = sheet2[0].getDataRange().getValues();
This line need only be executed once and I relocated it at the top of the code just after the various "filecombined" commands. As it stood, this line was being executed once for every student spreadsheet; this along may have contributed to several minutes of execution time.
2) I converted certain setvalue commands to an array, and then updated the "filecombined" spreadsheet from the array once only, at the end of the processing. Depending on the number of students with low credits and who are not already on the "filecombined" sheet, this may represent a substantial saving.
The code affected was lines 47 to 50.
line47: sheet2[0].getRange(lastrow+1, 1).setValue(nombre);
line48: sheet2[0].getRange(lastrow+1, 2).setValue(email);
line49: sheet2[0].getRange(lastrow+1, 3).setValue(credits);
line50: sheet2[0].getRange(lastrow+1, 4).setValue(fecha);
There are setvalue commands also executed at lines 38 and 39 (if the student is already on the "filecombined" spreadsheet), but I chose to leave these as-is. As noted above, we don't know how many such students there might be, and the cost of these setvalue commands may be minor or not. Until this is clear, and in the light of other time savings, I chose to leave them as-is.
function updated() {
//Final file data (Combined)
var filecombined = SpreadsheetApp.openById("XXXXXXXXXX");
var sheet2 = filecombined.getSheets();
//Take data from final file (Combined)
var data2 = sheet2[0].getDataRange().getValues();
// create some arrays
var Newdataarray = [];
var Masterarray = [];
//Folder with all the files
var parentFolder = DriveApp.getFolderById("YYYYYYYYYYYY");
var files = parentFolder.getFiles();
//Current Date
var fecha = new Date();
//Path for each file in the folder
while (files.hasNext()) {
var idarchivo = files.next().getId();
var sps = SpreadsheetApp.openById(idarchivo);
var sheet = sps.getSheetByName('STUDENT PROFILE');
var data = sheet.getDataRange().getValues();
var credits = data[5][1];
//Flat; bandera:1 (new row), bandera:2 (update row)
var bandera = 1;
//If credits are less than X: write
if (credits < 120){
var email = data[2][1];
var lastrow = filecombined.getLastRow();
var u = 0;
//comparison loop by email, if found it, update and exit the loop
while (u < lastrow) {
u = u + 1;
if (email == data2[u-1][1]){
sheet2[0].getRange(u, 3).setValue(credits);
sheet2[0].getRange(u, 4).setValue(fecha);
u = lastrow;
bandera = 2;
}
}
//if that email does not exist, write a new row
if(bandera == 1){
var nombre = data[0][1];
Newdataarray = [];
Newdataarray.push(nombre);
Newdataarray.push(email);
Newdataarray.push(credits);
Newdataarray.push(fecha);
Masterarray.push(Newdataarray);
}
}
}
// update the target sheet with the contents of the array
// these are all adding new rows
lastrow = filecombined.getLastRow();
sheet2[0].getRange(lastrow+1, 1, Masterarray.length, 4);
sheet2[0].setValues(Masterarray);
SpreadsheetApp.flush();
}
As I mentioned in my comment, the biggest issue you have is that you repeatedly search an array for a value, when you could use a much faster lookup function.
// Create an object that maps an email address to the (last) array
// index of that email in the `data2` array.
const knownEmails = data2.reduce(function (acc, row, index) {
var email = row[1]; // email is the 2nd element of the inner array (Column B on a spreadsheet)
acc[email] = index;
return acc;
}, {});
Then you can determine if an email existed in data2 by trying to obtain the value for it:
// Get this email's index in `data2`:
var index = knownEmails[email];
if (index === undefined) {
// This is a new email we didn't know about before
...
} else {
// This is an email we knew about already.
var u = ++index; // Convert the array index into a worksheet row (assumes `data2` is from a range that started at Row 1)
...
}
To understand how we are constructing knownEmails from data2, you may find the documentation on Array#reduce helpful.

Copy value of active cell to a different/non active sheet - How to determine which target/destination sheet to use

This script copies the value of the active cell, to another sheet based on the value in the cell next to it.
If I write the exact cell in my script, it works, but every time the script is run, it will be based on a different cell.
I also need to fine tune the destination.
Here it is:
function copytoTabs() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Booking In');
var data = sheet.getActiveCell();
var value = ss.getSheetByName('Booking In').getActiveCell().getA1Notation();
var operator = data.offset(0, 1).getValue();
if (operator == "Michelle") {
var ts = SpreadsheetApp.getActiveSpreadsheet();
var tss = ts.getSheetByName('MICHELLE Schedule');
ts.setActiveSheet(ts.getSheetByName('MICHELLE Schedule'));
tss.getRange(1, 2).activate();
tss.getRange(value).copyTo(tss.getActiveRange(),
SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
}
else if (operator == "Georgia") {
ss.setActiveSheet(ss.getSheetByName("GEORGIA Schedule"));
ss.getCurrentCell().offset(0, 1, 4, 1).activate();
ss.getRange('\'Booking In\'!P12').copyTo(ss.getActiveRange(),
SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
}
else if (operator == "Julie") {
ss.setActiveSheet(ss.getSheetByName("JULIE Schedule"));
ss.getCurrentCell().offset(0, 1, 4, 1).activate();
ss.getRange('\'Booking In\'!P12').copyTo(ss.getActiveRange(),
SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
}
ss.setActiveSheet(ss.getSheetByName('Booking In'), true);
}
Instead of using multiple if / else blocks, you can association a sheet tab name with the operator name in an object. Then look up the sheet tab name by operator name.
function copytoTabs() {
var activeCell,objectOfMappedValues,operator,sheet,sourceSs,targetSheetTabName,trgtSh;
sourceSs = SpreadsheetApp.getActiveSpreadsheet();
objectOfMappedValues = {//This is an object literal - the word literal means that the values are
//hard coded here in the function as opposed to being set with code
"Michelle":"MICHELLE Schedule",
"Georgia":"GEORGIA Schedule",
"Julie":"JULIE Schedule"
}
activeCell = sourceSs.getActiveCell();
Logger.log('activeCell: ' + activeCell)
operator = activeCell.offset(0, 1).getValue();//Get the value of one cell
targetSheetTabName = objectOfMappedValues[operator];//Get the sheet tab name for this operator
Logger.log('targetSheetTabName: ' + targetSheetTabName)
trgtSh = ts.getSheetByName(targetSheetTabName);//Get the sheet tab to be the target to set a value
Logger.log('trgtSh.getName(): ' + trgtSh.getName())
trgtSh.getRange(activeCell.getA1Notation()).copyTo(trgtSh.getActiveRange())
}
This code may not be everything that you are asking for, but hopefully it will advance you to the final solution.

Problems with insert a Timestamp

I have to insert a link with a sheet with the basic's of my original sheet.
=> At the original sheet there is a importrange which insert the data. In the next tab a query take the data - now there should be appear a timestamp when the data in col B (in the Sheet at the link) is updated, but only at the first time the col change from an empty col to a filled col.
I searching at the internet, but I didn't find a helpful answer. On the one hand the most of the Scripts I found didn't work in general or doing a little bit. As a example:
This script worked:
function onEdit(event)
{
var timezone = "GMT-5";
var timestamp_format = "MM-dd-yyyy";
var updatedColName = "Bid Responses";
var sheet = event.source.getSheetByName('Overview - Working (Hidden)');
var actRng = event.source.getActiveRange();
var editColumn = actRng.getColum();
var index = actRng.getRowIndex();
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues();
var dateCol = headers[0].indexOf(timeStampCoName);
var updateCol = headers[0].indexOf(updateColName); updateCol = updateCol+1;
if (dateCol > -1 && index > 1 && editColumn == updateCol) {
var cell = sheet.getRange(index, dateCol + 1);
var date = Utilities.formatDate(new Date(), timezone, timestamp_format);
cell.setValue(date);
}
}
But the timestamp was not inserted at the row where the col data change, it appear in a completely different row.
Can someone help me to write a Script that do exactly what I want?
Unru,
an onEdit trigger will always require a manual edit to the spreadsheet. In other words: the script runs when a user changes a value in a spreadsheet.A recalculation of a formula does NOT fire an onEdit script.
More info: here

Generate a Google Doc (and PDF) of row data when a cell in a row of a sheet is edited

I have a Google Form and Google Sheet set up to collect information I need. I currently have a script that will send me, and the form submitter, an email with a PDF attached that contains the contents of their form submission.
I am trying to edit that script and create another one that sends me a new version of the PDF after I go in and make a change to one of the cells associated with the original form submission (update the status of an issue, add notes, correct grammar, etc.).
This is what I have, I am still very new at programming and would appreciate any help...
(18 NOV #1425) It works! var last_column was not allowing the column after the edited cell to be defined. When I replaced last_column in var data with the actual number of columns that contained the data it worked great! Thank you to everyone who helped me figure this out, and learn a little along the way!
function onSheetEdit(e) {
var source = SpreadsheetApp.getActiveSpreadsheet();
var source_sheet = source.getSheetByName("Form Responses 1");
var range = source_sheet.getDataRange();
var ActiveRow = source_sheet.getActiveRange().getRow();
var data = source_sheet.getRange(ActiveRow,1,1,4).getValues();
var columnA = data[0][0];
var columnB = data[0][1];
var columnC = data[0][2];
var columnD = data[0][3];
(18 NOV #0800) Another friend suggested I change the beginning to this... The email also sends, and now I am getting "1" "1" "/" and "1" in my four document placeholders...
function onSheetEdit(e) {
var source_sheet = e.source.getActiveSheet();
if (source_sheet.getName() !== "Form Responses 1") return; //exit the script if edits are done on other sheets
var data = source_sheet.getRange(e.range.rowStart, 1, 1, source_sheet.getLastColumn())//(StartRow,StartColumn,NumberofRowstoGet,NumberofColumnstoGet)
.getValues()[0];
Logger.log(data);
var columnA = data[0][0];
var columnB = data[0][1];
var columnC = data[0][2];
var columnD = data[0][3];
(17 NOV #1845) I had a friend help me from work and this is as far as we got... The email sends now, but the placeholders are not populating the data correctly in the PDF file attachment. It appears that the only data that is populating is data from row 1, and of that data, only the cell that was edited, and the data in the cells to the right of it...
function onSheetEdit(e) {
var source = SpreadsheetApp.getActiveSpreadsheet();
var source_sheet = source.getSheetByName("Form Responses 1");
var range = source_sheet.getDataRange();
var last_column = source_sheet.getActiveRange().getColumn();
var ActiveRow = source_sheet.getActiveRange().getRow();
var data = source_sheet.getRange(ActiveRow,1,1,last_column).getValues();
var columnA = data[0][0];
var columnB = data[0][1];
var columnC = data[0][2];
var columnD = data[0][3];
(16 NOV #1700) I edited the script to this but still no emails generated. I get this error emailed to me when script fails: "Cell reference out of range (line 13, file "Copy of Form confirmation emails")". Line 13 is "var row".
function onSheetEdit() {
var source = SpreadsheetApp.getActiveSpreadsheet();
var source_sheet = source.getActiveSheet()
var row = source_sheet.getActiveCell().getRow();
var last_column = source_sheet.getLastColumn();
var data = source_sheet.getRange(row,1,1,last_column).getValues();
var columnA = data[0][0];
var columnB = data[0][1];
var columnC = data[0][2];
var columnD = data[0][3];
(16 NOV # 1330) I tried this instead but still no emails generated...
function onSheetEdit() {
var source = SpreadsheetApp.getActiveSpreadsheet();
var source_sheet = source.getActiveSheet()
var row = source_sheet.getActiveCell().getRow();
var last_column = source_sheet.getLastColumn();
var data = source_sheet.getRange(row,1,1,last_column);
var columnA = data.values[0];
var columnB = data.values[1];
var columnC = data.values[2];
var columnD = data.values[3];
Original script...
function onSheetEdit(e) {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var rows = sheet.getActiveCell().getRow();
var columnA = e.values[0];
var columnB = e.values[1];
var columnC = e.values[2];
var columnD = e.values[3];
var docTemplate = "1WyWeCLQQ3en1EbKjOLcWxlOLc0fHHDpZrB9yfXZ7nv8";
var docName = "Test form script";
var carbonCopyEmail = "jeffery.crane#goarmy.com";
var submitterEmail = columnB;
var dataName = columnC;
var submitDate = columnA;
var attachmentName = docName + ' for data ' + dataName
var submitterEmailPlaceholder = 'keyUsername';
var submitDatePlaceholder = 'keyTimestamp';
var templatePlaceholder1 = 'keyQuestion1';
var templatePlaceholder2 = 'keyQuestion2';
var submitterSubject = "Test Script Confirmation Email for data " + dataName;
var submitterBody = "Attached is a PDF confirmation sheet with the details of your submission of data: " + dataName + " submitted on " + submitDate;
var carbonCopySubject = "Test Script Submission Notification Email for data " + dataName;
var carbonCopyBody = "Attached is a PDF confirmation sheet with the details of " + submitterEmail + "'s submission of data: " + dataName + " on " + submitDate;
//Gets document template defined by the docID above, copys it as a new temp doc, and saves the Doc’s id
var copyId = DocsList.getFileById(docTemplate)
.makeCopy(attachmentName)
.getId();
//Open the temporary document
var copyDoc = DocumentApp.openById(copyId);
//Get the document’s body section
var copyBody = copyDoc.getActiveSection();
//POSSIBLE MODIFICATION TO ADD LINES OF CODE
//Replace place holder keys with the spreadsheet values in the google doc template
//This section of the script looks for instances where the key appears in the Google Doc and replaces the instance
//with the defined variable
//For instance, whenever "keyUserName" (defined above as submitterEmailPlaceholder) appears in the Google Doc,
//the value from the spreadsheet in columnB replaces "keyUserName"
copyBody.replaceText(submitDatePlaceholder, columnA);
copyBody.replaceText(submitterEmailPlaceholder, columnB);
copyBody.replaceText(templatePlaceholder1, columnC);
copyBody.replaceText(templatePlaceholder2, columnD);
//Save and close the temporary document
copyDoc.saveAndClose();
//Convert temporary document to PDF
var pdf = DocsList.getFileById(copyId).getAs("application/pdf");
//Attaches the PDF and sends the email to the form submitter
MailApp.sendEmail(submitterEmail, submitterSubject, submitterBody, {htmlBody: submitterBody, attachments: pdf});
//Attaches the PDF and sends the email to the recipients in copyEmail above
MailApp.sendEmail(carbonCopyEmail, carbonCopySubject, carbonCopyBody, {htmlBody: carbonCopyBody, attachments: pdf});
//Deletes the temporary file
DocsList.getFileById(copyId).setTrashed(true);
}
You cannot do data.values, data is a range object, you should do getValues() to get an array of values: data[row][col]
var data = source_sheet.getRange(row,1,1,last_column).getValues()
var columnA = data[0][0];
var columnB = data[1][0];
var columnC = data[2][0];
var columnD = data[3][0];
Why instead of mess manually with the cells values of the responses sheet you don't use the form edit link? You can put it into the mail you are already sending.
You can get it from:
var editLink = lastResponse.getEditResponseUrl();
When the form is submitted again from the edit link the script you wrote script will be run again sending the updated PDF.