I am using the enhanced workflow script that was posted by Mogsdad here.
I have managed to work out a few issues but one that I am stuck on at the moment is the error that comes up from this section -
// Record approval or rejection in spreadsheet
var row = ArrayLib.indexOf(data, 0, timestamp);
if (row < 0) throw new Error ("Request not available."); // Throw error if request was not found
sheet.getRange(row+1, approvalCol).setValue(e.parameter.approval);
I get the "Request not available" error because the ArrayLib.indexOf object is comparing the time stamp that is being presented from the same source but via two different 'routes'.
The timestamp from the 'timestamp' variable looks like this - "17/03/2015 18:00:11"
...and the timestamp contained in the 'data' variable (that should match the timestamp variable) looks like this - "Tue Mar 17 2015 00:30:10 GMT-0700 (PDT)".
I am assuming that the two different formats is what is resulting in the ArrayLib.indexOf object returning a '-1' result and hence the error message.
Any thoughts on what I need to do to get the matching working successfully ?
Create a new Date object for the timestamp value, so that you can ensure they can be compared. The code should look like:
var dateFromTimestamp = new Date(timestamp);
After looking around at a few other posts I came up with a solution that seems to work pretty well and overcomes the issues with using the timestamp.
I put an array formula in the first column of the response sheet that created a ticket number -
=ArrayFormula(if(B2:B,"AFR"&text(row(A2:A)-1,"00000"),iferror(1/0)))
Then I retrieved the ticket number (var cellVal) and sent it with the email. The response email brings the approval value to the correct line every time....so far.
function sendEmail(e) {
// Response columns: Timestamp Requester Email Item Cost
var email = e.namedValues["Requester Email"];
var item = e.namedValues["Item"];
var cost = e.namedValues["Cost"];
//var timestamp = e.namedValues["Timestamp"];
var row = e.range.getRow();
var seq = e.values[1];
var url = ScriptApp.getService().getUrl();
var sheet = SpreadsheetApp.openById('1pFL0CEW5foe8nAtk0ZwwTleYrBn2YulMu_eKPDEFQaw').getSheetByName("Form Responses 1");
var range = sheet.getDataRange();
var cellval = range.getCell(row,1).getValue();
//var origMail = range.getCell(row,3).getValue();
Logger.log(cellval);
//Logger.log(origMail);
var options = '?approval=%APPROVE%&reply=%EMAIL%'
.replace("%EMAIL%",e.namedValues["Requester Email"])
var approve = url+options.replace("%APPROVE%","Approved")+'&row='+row+'&cellval='+cellval;
var reject = url+options.replace("%APPROVE%","Rejected")+'&row='+row+'&cellval='+cellval;
var html = "<body>"+
"<h2>Please review</h2><br />"+
"Request from: " + email + "<br />"+
"Ticket No: " + cellval + "<br />"+
"For: "+item +", at a cost of: $" + cost + "<br /><br />"+
"Approve<br />"+
"Reject<br />"+
"</body>";
MailApp.sendEmail(Session.getEffectiveUser().getEmail(),
"Approval Request",
"Requires html",
{htmlBody: html});
}
Related
I am new to Google Apps Script. I have a sheet that collects some "Order Number" from form submit. I want to send mails through an event (On form submit) from my spreadsheet. The form will serve an order number. When the form is submitted, it will match the older submitted order numbers throughout the whole column. If it got matched once, the mail won't be sent. If it doesn't match then it will send a mail to the email address next to the order number.
The email address will come from another sheet on the same spreadsheet using VLOOKUP. I managed to do this.
Sorry if I make any mistake with my English.
Edit:
I tried map() , filter() , indexOf() these methods. But I too new with this.
function search(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Copy of orderStatus");
var lr = ss.getLastRow() - 1;
var keyword = ss.getRange("H5").getValue();
var dataSource = ss.getRange(2, 2, lr, 1).getValues();
var mapped = dataSource.map(function(r){
return r[0]});
var showPos = mapped.indexOf(keyword) + 2;
var getMail = ss.getRange(showPos, 4).getValue();
var filted = mapped.filter(filterlogic);
}
var filterlogic = function(r){
if(r !== "zil20200010"){
return true;
} else {
return false;
}
}
On form submit, select the column (range) where you store all the order numbers and create a TextFinder and store it in a variable using the createTextFinder(findText) method for the specified range.
Get the TextFinder from the previous step and search the order number using the findNext() method.
If findNext() returns null then move to the next step. else, do nothing.
Get the email address to which you plan to send the order number.
After having the email address, use the sendEmail(recipient, subject, body, options) method to send the email. If you'd like, you can use HTML in the body to make it more professional.
For additional information, read:
the reference guide on creating TextFinders,
the reference guide on finding text using a TextFinder,
and the reference guide on GmailApp.
Sample code:
// imagine you store all the order numbers in column C, starting from row 2 to the last row in the column:
var emailRecipient = test#test.com;
var ordernumber = 123;
var RangeToSearch = sheet.getRange(2,3,sheet.getLastRow());
var TextFinder = RangeToSearch.createTextFinder(ordernumber);
var found = TextFinder.findNext();
if (found == null) {
MailApp.sendEmail({
to: emailRecipient,
subject: "New Order! Order Number: " + ordernumber,
htmlBody: html
});
}
First of all, thanks to all of you who helped me to reach this point. I found the solution to my problem after some "trial and error". I wanted to limit sending emails.
This code takes the Range. Get its values in an array. I mapped that array to act as a string. Then I added .pop() to that string, it removes our last/newly submitted data in that range. Then I used .includes() method to search my value in the mapped array, and assigned it to a variable called final (just came to my mind). This variable returns true/false depending on search results. If the order number does not exist then it returns false. After that, we set an if statement to execute our mailing function. If our order number does not match and return final as false our mailing function happens. Else it does nothing (means no email sents). And that's it!
Here is the code that solved my problem
function orderStatus(e) {
try {
var theirMail, subject, message;
var ourName, theirName;
var sSheet, orderNum, cosmetics, orderSts, phNum, lr,dataSource, mapped, final;
ourName = "My Company Name";
orderNum = e.namedValues["Order Number"].toString();
sSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("orderStatus");
lr = sSheet.getLastRow() - 1;
dataSource = sSheet.getRange(2, 2, lr).getValues();
mapped = dataSource.map(function(r){
return r[0].toString()});
mapped.pop();
final = mapped.includes(orderNum);
orderSts = sSheet.getRange(sSheet.getLastRow(),1).getValue();
theirMail = sSheet.getRange(sSheet.getLastRow(),4).getValue();
theirName = sSheet.getRange(sSheet.getLastRow(),5).getValue();
phNum = sSheet.getRange(sSheet.getLastRow(),6).getValue();
subject = "Order status notification from " + ourName + " to " + theirName;
if (final == false){
message =
"<div style='text-align: left; padding-left: 30px;'><h2>Dear <b>" + theirName +
",</b></h2><p>Your order no is <b><span style='font-size: 14px;'>" + orderNum +
"</span>.</b> <b><span style='font-size: 14px;'>Your order has been processed.</span>" +
"</b></p><p>We packaged your order and dropped it to the logistics. You will recieve phone call on <b><span style='font-size: 14px;'>" + phNum +
"</span></b> from logistics.<br>Thanks for purchasing from <b><span style='font-size: 14px;'>" + ourName +
"</span></b>.</p><p>Best regards,<br><b><span style='font-size: 14px;'>"+ourName+"</span></b></p></div>"+
"<p style='text-align: center;'><br><b>For further information please visit our facebook page <a href='https://www.facebook.com/' target='_blank' rel='noopener'>"+ourName+"</a>.</b></p><hr />";
textbody = message.replace("<br>", "\n\n");
cosmetics = {name: ourName, htmlBody: message};
MailApp.sendEmail(theirMail, subject, message, cosmetics);
}
}
catch (e) {
Logger.log(e.toString());
}
}
Okay, so I'm working on a script that will basically pull information from a spreadsheet to then send out in an email once an email address is added into the spreadsheet. The issue is I'm trying to add a noReply and cc setting but it seems to then break my script to where it keeps sending emails or doesn't send any at all.
Theory: I think 'cc' is throwing the issue as the script is reading it as a recipient and just sending the email out instead of using the recipient that's added into the spreadsheet. This is the reference I'm following: https://developers.google.com/apps-script/reference/mail/mail-app#sendEmail(String,String,String,Object)
This is my code with the issue section being commented as such:
// This constant is written in column M for rows for which an email
// has been sent successfully.
var EMAIL_SENT = 'EMAIL_SENT';
/**
* Sends non-duplicate emails with data from the current spreadsheet.
*/
function sendEmails2() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = 3; // Number of rows to process
// Fetch the range of cells
var dataRange = sheet.getRange(startRow, 1, numRows, 20);
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var emailAddress = row[8]; // Column I
var message = ('For the ticket below, please ensure that you call the user as the first resort before following up via email.' + '\n\n TimeStamp: ' + row[1] + '\n Agent Name: ' + row[2] + '\n Your site: ' + row[3] + '\n Zendesk Link: ' + row[4] + '\n Summary of User Issue: ' + row[5] + '\n De-Escalation Attempted:' + row[6] + '\n User Contact Number ' + row[7]); // Fetch of columns B-H
var emailSent = row[12]; // Column M
if (emailSent !== EMAIL_SENT) { // Prevents sending duplicates and everything after && emailAddress is just testing a fix but didn't work
var subject = 'You have been assigned a Supervisor Call. Please Action Within 24 hours!';
MailApp.sendEmail(emailAddress, subject, message, { // Section below gives the issue
noReply: true,
cc: 'cc#email.com'
});
sheet.getRange(startRow + i, 13).setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
}
Screenshot of spreadsheet layout:
enter image description here
This is my first time writing out a question on Stack but feel free to ask and I can detail more of what you need in order to help out.
I was able to get this working with Cooper's help. I had to do some small formatting changes to get it working but ultimately the code wasn't incorrect, just format issues that caused it to not be read appropriately.
Appreciate the help!
I have a Google Sheets script that runs onEdit of a specific cell, and it sets the value of 100+ cells, that are not in a linear range. I am using an individual setValue command for each cell, which is resulting in the function running extremely slowly (each cell takes just under a second to populate!).
I am guessing there is a more efficient way to accomplish this, and I'd love to hear any suggestions.
Here is an excerpt of my code (I've only included a small handful of the 100+ setValue commands):
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var dateselected = ss.getRange ("Data Sheet!A2").getValue();
var daterow = getdaterow(dateselected); //the row of the selected date
ss.getRange("Reconciliation!G3").setValue(ss.getRange("Data Sheet!D" + daterow).getValue());
ss.getRange("Reconciliation!K3").setValue(ss.getRange("Data Sheet!E" + daterow).getValue());
ss.getRange("Reconciliation!E7").setValue(ss.getRange("Data Sheet!F" + daterow).getValue());
ss.getRange("Reconciliation!G7").setValue(ss.getRange("Data Sheet!G" + daterow).getValue());
ss.getRange("Reconciliation!E8").setValue(ss.getRange("Data Sheet!H" + daterow).getValue());
ss.getRange("Reconciliation!G8").setValue(ss.getRange("Data Sheet!I" + daterow).getValue());
For context: a specified date is stored in Data Sheet!A2, which is used in function getdaterow to determine the row number to be used in setting the values.
Thankyou in advance!
You want to copy the value of one cell to one cell using Google Apps Script.
There are a lot of ranges for copying in your situation.
You want to reduce the process cost of your script.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
In this answer, I use 2 methods of spreadsheets.values.batchGet and spreadsheets.values.batchUpdate in Sheets API for reducing the process cost. In this case, your goal can be achieved by 2 API calls.
Modified script:
Please modify your script as follows. In this case, your current script in your question is modified. So when you use this at the actual situation, please modify object for your situation. And also, before you run the script, please enable Sheets API at Advanced Google services.
From:
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var dateselected = ss.getRange ("Data Sheet!A2").getValue();
var daterow = getdaterow(dateselected); //the row of the selected date
ss.getRange("Reconciliation!G3").setValue(ss.getRange("Data Sheet!D" + daterow).getValue());
ss.getRange("Reconciliation!K3").setValue(ss.getRange("Data Sheet!E" + daterow).getValue());
ss.getRange("Reconciliation!E7").setValue(ss.getRange("Data Sheet!F" + daterow).getValue());
ss.getRange("Reconciliation!G7").setValue(ss.getRange("Data Sheet!G" + daterow).getValue());
ss.getRange("Reconciliation!E8").setValue(ss.getRange("Data Sheet!H" + daterow).getValue());
ss.getRange("Reconciliation!G8").setValue(ss.getRange("Data Sheet!I" + daterow).getValue());
To:
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var dateselected = ss.getRange ("Data Sheet!A2").getValue();
var daterow = getdaterow(dateselected); //the row of the selected date
// I modified below script.
var object = [
{srcRange: "Data Sheet!D" + daterow, dstRange: "Reconciliation!G3"},
{srcRange: "Data Sheet!E" + daterow, dstRange: "Reconciliation!K3"},
{srcRange: "Data Sheet!F" + daterow, dstRange: "Reconciliation!E7"},
{srcRange: "Data Sheet!G" + daterow, dstRange: "Reconciliation!G7"},
{srcRange: "Data Sheet!H" + daterow, dstRange: "Reconciliation!E8"},
{srcRange: "Data Sheet!I" + daterow, dstRange: "Reconciliation!G8"},
];
var spreadsheetId = ss.getId();
var srcRanges = {ranges: object.map(function(e) {return e.srcRange})};
var srcValues = Sheets.Spreadsheets.Values.batchGet(spreadsheetId, srcRanges);
var resourceForBatchUpdate = {valueInputOption: "USER_ENTERED", data: object.map(function(e, i) {return {range: e.dstRange, values: srcValues.valueRanges[i].values}})};
Sheets.Spreadsheets.Values.batchUpdate(resourceForBatchUpdate, spreadsheetId);
At object, the source range and destination range are set to an array as an object. For example, ss.getRange("Reconciliation!G3").setValue(ss.getRange("Data Sheet!D" + daterow).getValue()); is {srcRange: "Data Sheet!D" + daterow, dstRange: "Reconciliation!G3"}.
Please modify object for your actual situation like this.
This modified script supposes that daterow returns the row number.
References:
Advanced Google services
Method: spreadsheets.values.batchGet
Method: spreadsheets.values.batchUpdate
If I misunderstood your question and this was not the direction you want, I apologize.
I need to get a sheet of data as an array, perform some processing, then save it to another sheet.
Without attempting any intermediate processing,
var booking = getRowsData(sheet); //should create the array
var range = target.getRange(lastDBRow+1,1,numRows); //obtains the destination range (numRows logged as 3)
then
range.setValues([booking]); //tries to save the values but throws an error "Incorrect range height, was 1 but should be 3"
Checking the booking object with
var response = ui.prompt(booking.length + " " + booking[0].length, ui.ButtonSet.OK);
shows length 3 and
booking[0].length (which I understood to be the columns) as undefined....
So why the row length mismatch?
And should the columns value be undefined?
My thanks in advance!
The full code is :
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName('New booking');
var headers = sheet.getRange(1, 1,1,sheet.getLastColumn());
var lastRow = sheet.getLastRow();
var numRows= lastRow-1
var target = ss.getSheetByName('Test'); //temp redirect to test sheet
var lastDBRow =target.getLastRow(); //we want to append the data after this
var booking = getRowsData(sheet); // Get data to copy as array object
= ui.prompt(booking.length + " " + booking[0].length, ui.ButtonSet.OK);
// Returns 3 Undefined
var response = ui.prompt(numRows, ui.ButtonSet.OK);
// Returns 3
var range = target.getRange(lastDBRow+1,1,numRows); // Must match range size
range.setValues([booking]);
}
Looks to me like there is no column count defined in this range:
var range = target.getRange(lastDBRow+1,1,numRows); // Must match range size
range.setValues([booking]);
Without being about to use debug, I can't confirm my idea, but I see you are defining the starting row, the starting column and the number of rows, but not the number of columns. I don't know for sure if that error message would be caused by that, but it looks like something to consider.
I'm writing a script for a google spreadsheet that should check to see whether the value of a particular cell is "Yes." If so, it should just end. If not, it should send an e-mail with the contents of several other cells to a defined address and write "Yes" to the designated cell. However, the if condition in the code seems to always evaluate as true even when it should evaluate as false. The script writes "Yes" to the designated cell and sends the e-mail. However, while the e-mail contains the contents of the designated cells, it's all out of order. Here's the script:
function sendNotification() {
var sheet = SpreadsheetApp.getActiveSheet();
var dataRange = sheet.getRange(2,1,1,6);
var data = dataRange.getValues();
var dateSubmitted = data[0];
var dateDue = data[1];
var url = data[2];
var currentText = data[3];
var newText = data[4];
var emailSent = data[5];
var test = 'Yes';
var emailAddress = 'harland.westgate#gmail.com';
var message = 'On ' + dateSubmitted + ' a colleague requested that ' + url + ' be changed to replace the text ' + currentText + ' with ' + newText + '. This change should be completed by ' + dateDue;
var subject = 'A website change has been requested';
if (emailSent!=test) { // Prevents sending duplicates
MailApp.sendEmail(emailAddress, subject, message);
sheet.getRange(2,6).setValue(test);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
The relevant cells are (sorry, tried to screenshot but rep is too low):
A2: 1/11/14
B2: 1/17/14
C2: www.google.com
D2: Lorem ipsum
E2: Dolor sit amit
F2: Yes
here's what it dumps into the body of the e-mail:
On Sat Jan 11 2014 00:00:00 GMT-0500 (EST),Fri Jan 17 2014 00:00:00 GMT-0500 (EST),www.google.com,Lorem ipsum,Dolor sit amit,Yes a colleague requested that undefined be changed to replace the text undefined with undefined. This change should be completed by undefined
I've searched extensively and have not found an explanation for either behavior. I don't think it's that the script is failing to read in the values of the cell range, because the e-mail body contains those values (just not where I was expecting them). Then again, in the places I was expecting those values, it drops in "undefined."
Remember getValues() returns a two-dimensional array of values.
Try:
...
var dateSubmitted = data[0][0];
var dateDue = data[0][1];
var url = data[0][2];
...